Files
VX5_KRO_GearPump/doc/DateItemModel改进分析.md
T

294 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DateItemModel 改进分析
## 1. 当前 DateItemModel 使用分析
### 1.1 结构定义
```cpp
struct DateItemModel {
QString datetimePattern;
int monthsOffset = 0;
int daysOffset = 0;
QStringList dataMap; // unused
QStringList daysMap;
QStringList monthsMap;
QMap<int, QString> yearsMap;
QStringList weeksMap;
QStringList hoursMap;
static DateItemModel createDefaultReplaceModel();
static void loadFromXml(DateItemModel& vm, const QXmlStreamAttributes& attrs);
static void saveToXml(const DateItemModel& vm, QXmlStreamAttributes& attrs);
};
```
### 1.2 主要使用场景
**PicDateItem 类**:
- 存储为成员变量 `m_model`
- 通过 `setModel(DateItemModel model)` 方法接收新模型(使用移动语义)
- 在 `setCustomAttrs` 和 `saveCustomAttrs` 中进行 XML 序列化
- 用于生成实时更新的日期时间字符串
**DateReplaceEditor 类**:
- 在 UI 编辑器中双向绑定数据
- 通过 `dateModel()` 返回完整的模型数据
- 通过 `setDateModel(const DateItemModel& model)` 接收模型
**DateVarSet 类**:
- 与 UI 界面进行双向数据同步
- 在 `dateModel()` 和 `setDateModel()` 中进行数据转换
## 2. 性能分析 (针对全志H618平台)
### 2.1 平台特性
- 全志H618是ARM架构处理器,主频约1.5GHz
- 通常配备1-2GB RAM
- 资源相对受限,需要优化内存和CPU使用
### 2.2 QStringList vs std::array 性能对比
#### 2.2.1 内存占用对比
- **QStringList**: 每个列表包含额外的元数据(引用计数、大小、容量等)
- dataMap (16个QString): ~16*(QString开销) + 指针数组开销
- daysMap (31个QString): ~31*(QString开销) + 指针数组开销
- monthsMap (12个QString): ~12*(QString开销) + 指针数组开销
- weeksMap (7个QString): ~7*(QString开销) + 指针数组开销
- hoursMap (24个QString): ~24*(QString开销) + 指针数组开销
- **std::array<QString, N>**: 直接内联存储,无额外指针
- 更好的缓存局部性
- 减少内存碎片
#### 2.2.2 访问性能
- **QStringList**: 间接访问(指针跳转)
- **std::array**: 直接访问(连续内存),更好的缓存命中率
#### 2.2.3 构造/析构性能
- **QStringList**: 共享数据,浅拷贝,但在赋值时可能触发深度拷贝
- **std::array**: 需要移动/拷贝每个元素
### 2.3 移动语义分析
#### 2.3.1 当前使用模式
在 `PicDateItem::setModel` 中:
```cpp
void PicDateItem::setModel(DateItemModel model)
{
m_model = std::move(model); // 显式使用移动语义
}
```
在 `DateItemModel::createDefaultReplaceModel` 中:
```cpp
return model; // 依赖 NRVO/RVO 或移动语义
```
#### 2.3.2 性能影响评估
对于小规模数组(N < 32):
- **QStringList 移动**: O(1) - 只需转移指针和引用计数
- **std::array<QString, N> 移动**: O(N) - 需要移动 N 个 QString
但要注意:
1. QString 本身实现了移动语义,移动是高效的操作
2. N 值较小(最大31个元素),移动开销可接受
3. 在 H618 上,移动 31 个 QString 的时间仍远小于毫秒级
## 3. 实际使用场景分析
### 3.1 频繁操作类型
- 读取操作: 高频(UI 显示、日期计算)
- 写入操作: 中频(用户编辑、配置变更)
- 复制/移动操作: 低频(对象传递、赋值)
### 3.2 性能瓶颈预测
- 读取操作: `std::array` 胜出(更好的缓存局部性)
- 写入操作: 两者相当
- 移动操作: `QStringList` 略优,但差距很小
## 4. 推荐方案
### 4.1 最优实现
```cpp
#include <array>
struct DateItemModel {
QString datetimePattern;
int monthsOffset = 0;
int daysOffset = 0;
std::array<QString, 16> dataMap; // 固定大小,类型安全
std::array<QString, 31> daysMap; // 固定大小,类型安全
std::array<QString, 12> monthsMap; // 固定大小,类型安全
std::array<QString, 7> weeksMap; // 固定大小,类型安全
std::array<QString, 24> hoursMap; // 固定大小,类型安全
std::array<QString, 16> yearsArray; // 年份数组,固定大小
int startYear; // 起始年份,用于计算索引对应的年份
};
```
### 4.2 实现注意事项
```cpp
// 在 ViewModel.cpp 中相应修改
DateItemModel DateItemModel::createDefaultReplaceModel()
{
DateItemModel model;
// 初始化 dataMap
const char* dataValues[] = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};
for (int i = 0; i < 16; ++i) {
model.dataMap[i] = QString(dataValues[i]);
}
// 初始化 daysMap
for (int i = 0; i < 31; ++i) {
model.daysMap[i] = QString("%1").arg(i + 1, 2, 10, QChar('0'));
}
// 初始化 monthsMap
for (int i = 0; i < 12; ++i) {
model.monthsMap[i] = QString("%1").arg(i + 1, 2, 10, QChar('0'));
}
// 初始化 weeksMap
const char* weekValues[] = {"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
for (int i = 0; i < 7; ++i) {
model.weeksMap[i] = QString(weekValues[i]);
}
// 初始化 hoursMap
for (int i = 0; i < 24; ++i) {
model.hoursMap[i] = QString("%1").arg(i, 2, 10, QChar('0'));
}
int startYear = QDate::currentDate().year();
for (int i = 0; i < 16; ++i) {
model.yearsMap.insert(startYear + i, QString::number(startYear + i));
}
return model;
}
```
## 5. 性能权衡结论
### 5.1 移动语义影响
在全志H618平台上:
- **移动开销**: 对于固定小数组,移动 31 个 QString 的开销非常小(微秒级)
- **访问优势**: `std::array` 提供更好的缓存局部性和访问性能
- **总体影响**: 类型安全和访问性能的收益远大于移动语义的轻微损失
### 5.2 最终建议
**推荐使用 `std::array`**,理由如下:
1. **类型安全**: 编译时确保固定大小,防止越界
2. **性能优势**: 更好的缓存局部性和访问性能
3. **内存效率**: 减少内存碎片,更紧凑的布局
4. **可维护性**: 清晰表达数据结构的固定大小特征
5. **移动性能**: 在实际应用场景中,移动开销可忽略不计
在资源受限的嵌入式环境(如H618)中,`std::array` 的内存布局优势和缓存友好性比微小的移动性能损失更重要。
## 6. 关于2.2KB结构体大小的分析
### 6.1 大小计算详情
改为 `std::array` 后的 `DateItemModel` 总大小约为:
- `QString datetimePattern`: ~24 字节
- `int monthsOffset`: 4 字节
- `int daysOffset`: 4 字节
- `std::array<QString, 16> dataMap`: 16 × ~24 = ~384 字节
- `std::array<QString, 31> daysMap`: 31 × ~24 = ~744 字节
- `std::array<QString, 12> monthsMap`: 12 × ~24 = ~288 字节
- `QMap<int, QString> yearsMap`: ~32 字节
- `std::array<QString, 7> weeksMap`: 7 × ~24 = ~168 字节
- `std::array<QString, 24> hoursMap`: 24 × ~24 = ~576 字节
**总计:约 2220 字节(~2.2KB)**
### 6.2 性能影响评估
在全志H618平台上:
- **复制时间估算**:2.2KB 复制约需 1-5 微秒
- **使用频率**:UI 编辑操作每秒最多几次,非常低频
- **CPU 占用**:即使每秒 100 次复制,也仅占 CPU 时间的 0.05%
### 6.3 可接受性结论
**2.2KB 结构体复制在当前代码中是完全可以接受的**,因为:
1. **性能影响微乎其微**:复制时间远小于其他系统操作(UI渲染、I/O等)
2. **使用频率极低**:实际复制操作非常不频繁
3. **硬件能力充足**:全志H618足以处理这种规模的数据复制
4. **代码简洁性**:值语义比智能指针语义更简单直观
5. **调试便利性**:值对象更容易调试和跟踪
## 7. 关于createDefaultReplaceModel函数的改进
随着我们将DateItemModel结构改为使用构造函数初始化,我们也可以移除createDefaultReplaceModel静态函数。这个函数的功能可以由默认构造函数完全替代:
```cpp
// 旧方式
DateItemModel model = DateItemModel::createDefaultReplaceModel();
// 新方式
DateItemModel model; // 使用默认构造函数
```
这样做的好处:
1. 简化了API:不再需要单独的工厂函数
2. 保持了一致性:所有实例都通过构造函数初始化
3. 减少了代码冗余:构造函数已经提供了相同的初始化逻辑
## 8. 关于传递方式的分析
### 8.1 当前传递方式
在 `PicDateItem::setModel(DateItemModel model)` 中,使用值传递但配合移动语义:
```cpp
void PicDateItem::setModel(DateItemModel model)
{
m_model = std::move(model); // 实际使用移动语义
}
```
### 7.2 传递方式对比
| 方式 | 复制开销 | 复杂性 | 适用场景 |
|------|----------|--------|----------|
| 值传递 + 移动 | 低(移动而非复制) | 低 | 推荐,适合当前场景 |
| const 引用 | 无传参开销 | 中 | 需要额外复制到成员 |
| 智能指针 | 8字节指针 | 高 | 需要共享数据时 |
## 9. 年份映射的优化改进
原来的 `QMap<int, QString> yearsMap` 被替换为 `std::array<QString, 16> yearsArray` 和 `int startYear` 的组合:
**优点:**
1. **内存效率更高**:`std::array` 比 `QMap` 占用更少内存
2. **访问速度更快**:数组访问比哈希表访问更快
3. **缓存友好**:连续内存布局提供更好的缓存性能
4. **类型安全**:编译时确定大小,防止越界
**实现方式:**
- `startYear` 存储起始年份(如 2023)
- `yearsArray[0]` 对应 `startYear` 年(2023年)
- `yearsArray[1]` 对应 `startYear + 1` 年(2024年)
- 以此类推,最多支持16个连续年份
**访问逻辑:**
```cpp
// 访问特定年份的映射值
int year = 2025;
if (year >= startYear && year < startYear + 16) {
int index = year - startYear;
QString mappedValue = yearsArray[index];
}
```
### 7.3 推荐方案
保持当前的值传递设计,利用移动语义获得最佳性能:
```cpp
// 调用时使用移动
DateItemModel model = DateItemModel::createDefaultReplaceModel();
picItem.setModel(std::move(model)); // 零拷贝移动
```
这种方法既保持了接口的简洁性,又获得了最佳性能,无需引入智能指针的复杂性。