问题描述
证券公司的量化监控端显示某一用户的cpu占用率超过了1000%!
解决方案
ptrade策略CPU占用率超1000%的原因分析与优化指南
在Linux/Unix系统中,CPU占用率是按核心数计算的。CPU占用率超过1000%,意味着你的策略同时跑满了至少10个CPU核心。在ptrade量化引擎中,这种情况通常是由多线程堆积、死循环、或极高频的重度计算引起的。以下是常见的排查方向及优化方案:
1. run_interval 导致的多线程堆积(最常见原因)
原因分析:
ptrade的run_interval(context, func, seconds)是以多线程并行运行的。如果你设置的间隔时间极短(例如3秒),但func函数内部的逻辑执行时间超过了3秒(比如进行了大量的数据请求或复杂的Pandas计算),就会导致上一个线程还没执行完,下一个线程又被拉起。随着时间推移,后台会堆积成百上千个线程,瞬间吃满所有CPU核心。
优化方案:
- 增加间隔时间:评估函数实际执行时间,适当调大
seconds参数。 - 精简周期函数逻辑:在
run_interval中只做最轻量级的状态检查,将重度计算移至before_trading_start或run_daily中。
2. 主推回调函数中的无限递归(死循环)
原因分析:
在on_order_response(委托主推)或on_trade_response(成交主推)中调用了下单接口(如order)。如果逻辑判断不严谨,会导致:下单 -> 触发主推 -> 主推中再次下单 -> 再次触发主推……形成死循环,瞬间耗尽CPU资源。
优化方案:
- 增加状态锁:在全局变量
g中设置标志位(如g.order_flag),确保同一逻辑只触发一次下单。 - 严格的条件判断:在主推回调中,务必判断订单状态(如是否已成、是否废单),避免盲目追单。
3. 高频调用耗时API且未使用字典模式
原因分析:
在handle_data(分钟级)或tick_data(3秒级)中,频繁调用get_history、get_price、get_individual_entrust等接口获取全市场或大批量股票的数据。默认情况下,这些接口返回的是Pandas DataFrame或Panel对象,底层构建这些对象极其消耗CPU。
优化方案:
- 开启字典模式:在调用API时传入
is_dict=True。官方文档明确指出,返回dict类型数据的速度比DataFrame有大幅提升,能显著降低CPU开销。 - 减少调用频率:不要在
tick_data中请求长周期的历史K线,历史数据应在盘前获取并缓存到全局变量g中。
4. 复杂的Pandas/Numpy运算未向量化
原因分析:
在盘中高频回调中,对大型DataFrame使用了for循环遍历(如iterrows()),或者进行了极其复杂的矩阵运算。
优化方案:
- 使用向量化操作:利用Pandas/Numpy的内置向量化函数替代
for循环。 - 预计算:能在盘前(
before_trading_start)计算好的因子,绝不拖到盘中(handle_data)计算。
5. 频繁的I/O操作
原因分析:
在盘中高频打印海量日志(log.info),或者频繁读写本地文件(如使用pickle或csv频繁落地数据)。
优化方案:
- 控制日志输出:去除调试用的高频日志,只保留关键的交易信号和报错日志。
- 优化持久化频率:ptrade框架本身会在特定事件后自动持久化
g变量,尽量依赖框架机制,减少手动高频写文件。
紧急处理建议
- 立即停止策略:联系券商或在终端立即停止该策略,避免影响同一服务器上其他用户的交易。
- 检查日志:查看策略停止前的最后几百行日志,寻找是否有高频重复的报错或死循环打印。
- 代码Review:重点排查
run_interval、on_order_response以及tick_data中的逻辑。