迭代 120-优化性能 1¶
背景¶
development 分支由 master 去掉元编程等技术后重构而来,但整体性能下降约 20%。本迭代基于 profile 日志对比,定位主要回退点并给出可落地的优化建议(先写建议,后续再逐个落地实现并复测)。
对比结论(整体)¶
master 总耗时: 553.1201s(logs/performance_profile_master_20260116_214606.log)
development 总耗时: 663.5783s(logs/performance_profile_development_20260116_214820.log)
回退幅度: +110.4582s(约 +19.97%)
主要回退点(按影响排序)¶
1) runonce 路径整体变慢:Cerebro._runonce 明显回退¶
表现:
master:
cerebro.py:_runonce累计 228.14sdevelopment:
cerebro.py:_runonce累计 302.84s差值: +74.70s
关键关联热点:
development 中
lineiterator.py:_once累计 50.43s(master 中同类逻辑累计约 11.85s)development 中若干指标的
once()实现耗时显著(尤其是布林带)
初步原因判断:
LineIterator._once的实现与 master 相比引入了更多 Python 层逻辑(循环、hasattr/getattr 分支、重复 oncebinding 传播等),导致 runonce 阶段子调用开销放大。
2) broker 通知链路变慢:Cerebro._brokernotify / BackBroker.next 回退¶
表现:
master:
cerebro.py:_brokernotify累计 24.05sdevelopment:
cerebro.py:_brokernotify累计 57.44s差值: +33.40s
关键关联热点:
master:
bbroker.py:next累计 21.39sdevelopment:
bbroker.py:next累计 53.24sdevelopment:
bbroker.py:_get_value自身耗时 8.10s(master 同类逻辑约 3.43s)
初步原因判断:
BackBroker在热点路径里多次get_param(),以及_get_value()循环内重复取参。development 中引入的 Python 层
__getattribute__覆盖(见下一条)会放大 broker 上的所有属性访问成本。
3) __getattribute__ 覆盖带来大量 Python 层属性访问开销(高频、可直接优化)¶
BackBroker.getattribute:
development:
bbroker.py:__getattribute__自身耗时 11.21s(36.92M calls)master: 无此热点(未覆盖
__getattribute__)
CommInfoBase.getattribute:
development:
comminfo.py:__getattribute__自身耗时 5.80s(11.73M calls)master: 无此热点(未覆盖
__getattribute__)
初步原因判断:
覆盖
__getattribute__会让对象的“所有属性访问”从 C-level 变成 Python-level 调用,哪怕只针对一个字段做特殊处理,也会把整类对象的属性读取开销整体抬升。
4) 内置函数调用量显著上升(属于“症状”,多由上面几条触发)¶
isinstance:
master: 45.38M calls / 7.49s
development: 104.29M calls / 13.78s
hasattr:
master: 4.46M calls / 0.94s
development: 38.47M calls / 8.32s
math.isnan:
development: 53.10M calls / 4.75s(master 未出现为热点)
初步原因判断:
大量
hasattr/getattr/isinstance/math.isnan通常来自“为了兼容/防御式编程”在热循环里做了过多检查;需要把这些检查挪到初始化阶段缓存结果,或用更轻量的判定方式。
改进建议(可落地、按优先级)¶
建议 A(最高优先级):移除/替换 __getattribute__ 覆盖,只对目标字段做“局部拦截”¶
目标: 直接吃掉 development 中 10s+ 级别的纯开销,并减少连带的 getattr/hasattr/isinstance 放大效应。
落地方向:
BackBroker.cash:
不使用
BackBroker.__getattribute__。改为“仅对 cash 做拦截”的 descriptor/property(例如自定义
cashdescriptor:__get__优先返回_cash,__set__同步更新_cash与参数系统)。
CommInfoBase.stocklike:
不使用
CommInfoBase.__getattribute__。同样用 property/descriptor,或在
_post_init_setup()后把最终_stocklike回写进参数系统,保证self.stocklike直接命中 param_manager 的快路径。
建议 B(高优先级):对齐/精简 LineIterator._once(runonce 热点),避免重复 oncebinding/多余分支¶
目标: 缩小
LineIterator._once带来的子调用累积开销(当前 development: 50.43s vs master: ~11.85s)。落地方向:
尽量恢复 master 的结构:
一次性 forward(避免在 _once 内做过多动态 end 计算)
指标一次性
_once()home/reset 的顺序与 master 保持一致
oncebinding 传播尽量“在必要的层级做一次”(避免每个 indicator 再嵌套多轮 hasattr/遍历)
避免在热路径里
hasattr(lines_obj, '__iter__')/hasattr(line, 'oncebinding')这种重复检查:可以在对象构建阶段缓存“是否需要 oncebinding”
或直接按约定类型调用,让异常暴露(EAFP)并在外层处理
建议 C(高优先级):broker 热循环内减少 get_param()/字典查找次数(局部缓存)¶
目标: 降低
BackBroker.next/_get_value中的参数系统访问成本。落地方向:
在
_get_value()开头缓存:shortcash = self.get_param('shortcash')positions = self.positionsgetcommissioninfo = self.getcommissioninfo
在
next()开头缓存:checksubmit = self.get_param('checksubmit')
复用局部变量,减少重复属性查找(Python 解释器层面收益很可观)。
建议 D(中优先级):减少 math.isnan/isinstance 在热路径里的调用¶
目标: 直接降低
math.isnan53M 次调用,以及isinstance的额外增量。落地方向:
对数值序列,优先用
x != x判断 NaN(NaN 的特性)替代isinstance(x, float) and math.isnan(x)。将“是否 datetime line / indicator line”的判断在初始化时缓存(目前部分模块已有缓存,但仍有大量零散判断)。
建议 E(中优先级):优化布林带等指标的 once() 算法复杂度¶
表现: development 中
indicators/bollinger.py:once自身耗时 12.98s(12 calls)。落地方向:
将
once()内的双重循环(N*period)改为滑动窗口:维护 rollingsum与sum_sq,每步 O(1) 更新。或回退到 master 的“组合指标实现(SMA + StdDev)”,但需确认与当前去元编程后的绑定/传播机制兼容。
建议 F(可选/评估项):参数系统的 history/callback 默认关闭¶
目标: 降低参数系统在生产 backtest 中的额外能力开销。
落地方向:
允许在
ParameterizedBase/ParameterManager初始化时传入enable_history=False, enable_callbacks=False(默认关闭,测试或 debug 时再开启)。
验证方案(你后续修改代码后执行)¶
安装验证:
pip install -U .
格式化/质量:
bash scripts/optimize_code.sh
单测:
运行现有测试用例(pytest / run_tests.sh),确保全部通过
性能复测:
python scripts/profile_performance.py --processes 12生成新的 profile log 后,与以下基准对比:
master:
logs/performance_profile_master_20260116_214606.log当前 development:
logs/performance_profile_development_20260116_214820.log