调整部分样式

This commit is contained in:
2026-05-11 09:11:30 +08:00
parent e40a72cd7c
commit c8a5a121c9
31 changed files with 2763 additions and 660 deletions
+294
View File
@@ -0,0 +1,294 @@
# 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)); // 零拷贝移动
```
这种方法既保持了接口的简洁性,又获得了最佳性能,无需引入智能指针的复杂性。