运算任务愈发繁重,如何加速Python程序运行?

论坛 期权论坛 期权     
真格量化   2019-8-12 09:23   6155   0
问题


假设您发现自己的Python程序运行太慢,比如您想在真格量化中进行tick交易时,在两个tick间隔的500毫秒之内进行大量的运算,您想在不使用复杂技术,比如C扩展或JIT编译器的情况下加快程序运行速度。有什么方法呢?


解决方案


关于程序优化的第一个准则是“不要优化”(程序也许还能胜任工作),第二个准则是“不要优化那些无关紧要的部分”。如果您的程序运行缓慢,首先我们可以使用一些性能分析技术先对它进行性能测试找到问题所在。


通常来讲您会发现你得程序在少数几个“热点”部分花费了大量时间, 比如内存的数据处理循环。一旦您定位到这些点,您就可以使用下面这些实用技术来加速程序运行。











使用函数


很多程序员刚开始会使用Python语言写一些简单脚本。当编写脚本的时候,通常习惯了写毫无结构的代码,比如:



很少有人知道,像这样定义在全局范围的代码运行起来要比定义在函数中运行慢的多。这种速度差异是由于局部变量和全局变量的实现方式(使用局部变量要更快些)。因此,如果您想让程序运行更快些,只需要将脚本语句放入函数中即可:

速度的差异取决于实际运行的程序,不过根据经验,使用函数带来15-30%的性能提升是很常见的。


尽可能去掉属性访问


每一次使用点(.)操作符来访问属性的时候会带来额外的开销。它会触发特定的方法,比如 __getattribute__() 和 __getattr__() ,这些方法会进行字典操作操作。


通常你可以使用 from module import name 这样的导入形式,以及使用绑定的方法。假设您有如下的代码片段:

在我们机器上面测试的时候,这个程序花费了大概40秒。现在我们修改 compute_roots() 函数如下:



修改后的版本运行时间大概是29秒。唯一不同之处就是消除了属性访问。用 sqrt() 代替了 math.sqrt() 。The result.append() 方法被赋给一个局部变量 result_append ,然后在内部循环中使用它。


不过,这些改变只有在大量重复代码中才有意义,比如循环。因此,这些优化也只是在某些特定地方才应该被使用。


理解局部变量


之前提过,局部变量会比全局变量运行速度快。对于频繁访问的名称,通过将这些名称变成局部变量可以加速程序运行。例如,看下之前对于 compute_roots() 函数进行修改后的版本:

在这个版本中,sqrt 从 match 模块被拿出并放入了一个局部变量中。如果您运行这个代码,大概花费25秒(对于之前29秒又是一个改进)。这个额外的加速原因是因为对于局部变量 sqrt 的查找要快于全局变量 sqrt。


对于类中的属性访问也同样适用于这个原理。通常来讲,查找某个值比如 self.name 会比访问一个局部变量要慢一些。在内部循环中,可以将某个需要频繁访问的属性放入到一个局部变量中。例如:



避免不必要的抽象


任何时候当您使用额外的处理层(比如装饰器、属性访问、描述器)去包装您的代码时,都会让程序运行变慢。












比如看下如下的这个类:

现在进行一个简单测试:



可以看到,访问属性y相比属性x而言慢的不止一点点,大概慢了4.5倍。如果您在意性能的话,那么就需要重新审视下对于y的属性访问器的定义是否真的有必要了。如果没有必要,就使用简单属性吧。如果仅仅是因为其他编程语言需要使用getter/setter函数就去修改代码风格,这个真的没有必要。


使用内置的容器


内置的数据类型比如字符串、元组、列表、集合和字典都是使用C来实现的,运行起来非常快。如果您想自己实现新的数据结构(比如链接列表、平衡树等), 那么要想在性能上达到内置的速度几乎不可能,因此,还是乖乖的使用内置的吧。


避免创建不必要的数据结构或复制


有时候程序员想显摆下,构造一些并没有必要的数据结构。例如,有人可能会像下面这样写:


values = [x for x in sequence]


squares = [x*x for x in values]





也许这里的想法是首先将一些值收集到一个列表中,然后使用列表推导来执行操作。不过,第一个列表完全没有必要,可以简单的像下面这样写:


squares = [x*x for x in sequence]



与此相关,还要注意下那些对Python的共享数据机制过于偏执的开发者所写的代码。有些人并没有很好的理解或信任Python的内存模型,滥用 copy.deepcopy() 之类的函数。通常在这些代码中是可以去掉复制操作的。


讨论


在优化之前,有必要先研究下使用的算法。选择一个复杂度为 O(n log n) 的算法要比您去调整一个复杂度为 O(n**2) 的算法所带来的性能提升要大得多。


如果您觉得您的程序还是得进行优化,那么请从整体考虑。作为一般准则,不要对程序的每一个部分都去优化,因为这些修改会导致代码难以阅读和理解。您应该专注于优化产生性能瓶颈的地方,比如内部循环。


您还要注意微小优化的结果。例如考虑下面创建一个字典的两种方式:



后面一种写法更简洁一些(您不需要在关键字上输入引号)。不过,如果你将这两个代码片段进行性能测试对比时,会发现使用 dict() 的方式会慢了3倍。看到这个,您是不是有冲动把所有使用 dict() 的代码都替换成第一种。不够,聪明的程序员只会关注他应该关注的地方,比如内部循环。在其他地方,这点性能损失没有什么影响。


如果您的优化要求比较高,本篇文章的这些简单技术满足不了,那么您可以研究下基于即时编译(JIT)技术的一些工具。例如,PyPy工程是Python解释器的另外一种实现,它会分析您的程序运行并对那些频繁执行的部分生成本机机器码。它有时候能极大的提升性能,通常可以接近C代码的速度。您还可以考虑下Numba工程, Numba是一个在您使用装饰器来选择Python函数进行优化时的动态编译器。这些函数会使用LLVM被编译成本地机器码。它同样可以极大的提升性能。


最后我们可以引用John Ousterhout说过的话作为结尾:“最好的性能优化是从不工作到工作状态的迁移”。直到您真的需要优化的时候再去考虑它。确保您程序正确地运行通常比让它运行得更快要更重要一些(至少开始是这样的).




— — — — — — E N D — — — — — —
往期文章:
Numpy处理tick级别数据技巧
真正赚钱的期权策略曲线是这样的
多品种历史波动率计算
如何实现全市场自动盯盘
AI是怎样看懂研报的
真格量化策略debug秘籍
真格量化对接实盘交易
常见高频交易策略简介

如何用撤单函数改进套利成交

Deque提高处理队列效率

策略编程选Python还是C++

如何用Python继承机制节约代码量

十大机器学习算法
如何调用策略附件数据

如何使用智能单

如何扫描全市场跨月价差

如何筛选策略最适合的品种

活用订单类型规避频繁撤单风险

真格量化回测撮合机制简介

如何调用外部数据

如何处理回测与实盘差别

如何利用趋势必然终结获利

常见量化策略介绍

期权交易“七宗罪”

波动率交易介绍

推高波动率的因素

波动率的预测之道

趋势交易面临挑战
如何构建知识图谱
机器学习就是现代统计学

AI技术在金融行业的应用

如何避免模型过拟合

低延迟交易介绍

架构设计中的编程范式

交易所视角下的套利指令撮合

距离概念与特征识别

气象风险与天气衍生品

设计量化策略的七个“大坑”

云计算在金融行业的应用

机器学习模型评估方法
真格量化制作期权HV-IV价差
另类数据介绍

TensorFlow中的Tensor是什么?

机器学习的经验之谈

用yfinance调用雅虎财经数据

容器技术如何改进交易系统
Python调用C++
如何选择数据库代理
统计套利揭秘

[h1]一个Call搅动市场?让我们温习一下波动率策略[/h1][h1]如何用真格量化设计持仓排名跟踪策略[/h1][h1]还不理解真格量化API设计?我们不妨参考一下CTP平台[/h1][h1]理解同步、异步、阻塞与非阻塞[/h1][h1]隐波相关系数和偏度——高维风险的守望者[/h1]Delta中性还不够?——看看如何设计Gamma中性期权策略

[h1]Python的多线程和多进程——从一个爬虫任务谈起[/h1]线程与进程的区别
皮尔逊相关系数与历史K线匹配

Python2和Python3的兼容写法
Python代码优化技巧

理解Python的上下文管理器

如何写出更好的Python代码?这是Python软件基金会的建议
评估程序化模型时我们容易忽视的指标

看看如何定位Python程序性能瓶颈

什么是Python的GIL

投资研究中的大数据分析趋势及应用

理解CTP中的回调函数

如何围绕隐含波动率设计期权交易策略                    
看看如何用Python进行英文文本的情感分析
算法交易的分类

Python编码的最佳实践总结

什么是波动率锥?如何用波动率锥设计期权策略?
期权的波动率策略与时间价值收集策略对比

期权用于套期保值和无风险套利

隐含波动率对期权策略的影响

卖出期权交易的风险管理原则和技巧
期权交易中的“大头针”风险
期权做市商策略简介

精细化您的交易——交易成本评估与交易执行策略
海外市场交易执行策略的实践
设计期权套期保值方案时应注意的问题
美式期权、欧式期权比较分析——定价与风险管理
构建您的AI时代武器库——常用的机器学习相关Python库
期权波动率“微笑曲线”之谜


真格量化可访问:
https://quant.pobo.net.cn


真格量化微信公众号,长按关注:

遇到了技术问题?欢迎加入真格量化Python技术交流QQ群  726895887


































分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:870
帖子:180
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP