迭代 127 - 性能优化 8¶
背景¶
基于 performance_profile_development_20260117_141659.log 分析,当前总执行时间 343.38 秒。
性能分析¶
主要热点函数 (按 tottime 排序)¶
| 排名 | 函数 | tottime | 调用次数 | per call |
|——|——|———|———-|———-|
| 1 | lineseries.__setattr__() | 14.34s | 21.3M | 0.67μs |
| 2 | linebuffer.forward() | 11.73s | 10.7M | 1.09μs |
| 3 | linebuffer.__setitem__() | 8.60s | 10.7M | 0.81μs |
| 4 | linebuffer.__getitem__() | 7.67s | 19M | 0.40μs |
| 5 | builtins.len | 7.63s | 38.7M | 0.20μs |
| 6 | dateintern.num2date() | 7.46s | 2.2M | 3.35μs |
| 7 | drawdown.next() | 6.82s | 688K | 9.91μs |
| 8 | linebuffer.advance() | 6.02s | 9.1M | 0.66μs |
| 9 | linebuffer.set_idx | 5.56s | 23.3M | 0.24μs |
| 10 | strategy._notify() | 5.51s | 688K | 8.01μs |
高调用次数函数¶
| 函数 | 调用次数 | tottime |
|——|———-|———|
| builtins.len | 38.7M | 7.63s |
| linebuffer.get_idx | 30.5M | 3.42s |
| builtins.getattr | 27.5M | 5.16s |
| linebuffer.set_idx | 23.3M | 5.56s |
| builtins.hasattr | 18.7M | 3.99s |
| str.startswith | 17.3M | 2.67s |
优化建议¶
Phase 1: 立即实施 (低风险)¶
1.1 linebuffer.py:set_idx/get_idx - 使用属性替代方法调用¶
问题*:
set_idx和get_idx是非常简单的方法,但被调用了 53.8M 次,方法调用开销显著。当前代码*:
def get_idx(self):
return self.idx
def set_idx(self, idx):
self.idx = idx
```bash
- *优化方案**: 使用 `@property` 或直接访问 `self.idx` 属性
- *预期收益**: 减少方法调用开销,节省约 2-3 秒
#### 1.2 `linebuffer.py:__getitem__()` - 简化快速路径
- *问题**: `__getitem__` 被调用 19M 次,每次都有多重条件判断
- *优化方案**: 对最常见的 `ago=0` 情况添加快速路径
- *预期收益**: 减少条件判断,节省约 1-2 秒
#### 1.3 `linebuffer.py:__setitem__()` - 减少边界检查
- *问题**: `__setitem__` 被调用 10.7M 次,边界检查开销大
- *优化方案**: 对常见情况简化边界检查
- *预期收益**: 减少计算,节省约 1 秒
### Phase 2: 中等风险优化
#### 2.1 `dateintern.py:num2date()` - 添加 LRU 缓存
- *问题**: `num2date` 被调用 2.2M 次,每次都进行日期转换计算
- *优化方案**: 使用 `functools.lru_cache` 缓存常见日期转换
- *预期收益**: 减少重复计算,节省约 3-4 秒
#### 2.2 `drawdown.py:next()` - 优化计算逻辑
- *问题**: `drawdown.next()` 被调用 688K 次,tottime 6.82s
- *优化方案**: 缓存中间计算结果,减少重复访问
- *预期收益**: 节省约 2 秒
### Phase 3: 高风险优化 (需要仔细测试)
#### 3.1 减少 `hasattr` 调用
- *问题**: `hasattr` 被调用 18.7M 次,耗时 3.99s
- *优化方案**: 使用 `getattr(obj, attr, None)` 替代 `hasattr` + 访问
#### 3.2 减少 `len()` 调用
- *问题**: `len()` 被调用 38.7M 次,耗时 7.63s
- *优化方案**: 缓存长度值,避免重复计算
## 测试验证
每次优化后需要运行:
```bash
# 1. 安装更新并运行测试
pip install -U . && pytest tests -n 8
# 2. 代码格式检查
./scripts/optimize_code.sh
# 3. 性能测试
python scripts/profile_performance.py
```bash
## 实施记录
| 日期 | 优化项 | 状态 | 效果 |
|------|--------|------|------|
| | | | |