天天看点

python实战 - 多核多进程发挥multiprocessing的强大威力

作者:A股程序化交易

本文是笔者开发量化交易系统过程中,使用python对大批量数据进行高强度、高密度的计算,其中使用了多线程和多核处理函数的实践体验,对python在多线程处理和多核多进程处理方面的比较得出的结论:python在多线程处理方面并不出彩,实际上是伪多线程,使用其多线程函数在高密度计算或对事件的快速响应方面,性能并没有比单线程有很大的提高,远不如C/C++/C#等这类编程语言那么强大;而利用python的多核处理函数,进行高密度计算或快速事件响应的能力非常突出。

特别说明,本文仅为笔者在具体项目开发中的体验,记录下来和同行交流,没有一般性的指导意义;另外,这段时间头条文章编辑不能使用代码块,故不能发布完整的代码,仅用相关插图来说明。

本文背景描述

两年前笔者把量化交易系统由matlab版改写为python版,其原因就是matlab的单线程不能满足量化交易系统每1分钟运行一次的需求。该系统需要在1分钟内,要完成从接收A股全市场行情数据,到合成各级时间K线数据并保存,再到执行交易策略,最后输出交易信号并作出快速响应的完整过程。在整个过程中,其中的每个环节都需要完成高密度的计算,那么发挥多线程或多核多进程的能力就是必须的选择。本文只简述该系统中每分钟接收和处理A股全市场行情数据这一环节,如何利用多核多进程实现数据快速处理。每分钟接收到的A股行情数据一般时段大约8万条,开盘几分钟大约10万条;根据系统要求,接收和处理数据必须在20秒内完成,否则将影响系统的后续运行。

电脑硬件配置

为此,笔者在电脑城装了一台多核电脑,使用“洋垃圾”服务器芯片,配了两颗Intel(R) Xeon(R) CPU E5-2660 v2 @ 2.20GHz 2.20 GHz处理器,每颗处理器20核共40核,32G内存,无数据计算能力的普通显卡。起初也是对这种电脑的性能如何没有经验,反正花钱不多,就组装一台试试吧。该电脑硬件如下图:

python实战 - 多核多进程发挥multiprocessing的强大威力
python实战 - 多核多进程发挥multiprocessing的强大威力

在A股交易时段,接收并处理实时行情数据

A股在开盘期间,全推实时行情数据量非常大,也非常密集,现在4小时交易时间内,产生的实时行情数据总量约1800万条,另外在行情火爆的时候一分钟能到10万条,开盘几分钟的交易产生的数据大约都在10万条以上,这种场景下正是多核多进程处理数据的时候。

笔者在该系统外,用C#开发了专门接收实时行情数据的小工具,并经过初步聚合(在每个个股的实时数据之间插入字母A作为后续分割标志,聚合后的每条数据不超过100个个股数据,以便减少写入redis的次数),在写入redis保存,再由该系统把前一分钟的数据全部读入并进行后续处理和使用。写入redis的数据格式如下图:

python实战 - 多核多进程发挥multiprocessing的强大威力

合成个股数据的程序代码

这一部分是量化交易系统中第一个需要快速处理数据的环节,要从redis中读入前一分钟的所有实时行情数据,立即筛选出当天要监听股票的数据,并合成所有监听股票的1分钟的开盘价/最高价/最低价/收盘价/成交量数据。处理代码示例如下图:

python实战 - 多核多进程发挥multiprocessing的强大威力

上图给出了处理某一分钟接收并保存在redis中的实时行情的完整示例。以下为重点部分说明:

  • 标号4:为了简便起见,标号4所在的列表中只选取了5条redis内存数据库中的quote数据;
  • 标号3:该标号下面几行代码是读取监听股票的股票代码(ZZ800_1000为中证800和中证1000的股票);
  • 标号1和标号2:所指的两个自定义函数filtering_quotes(onequote_watchingsymbollist)和multiprocess_filteringquotes(quote_watchingsymbol_list)共同实现多处理器并行处理数据的功能,其所属工具包multiprocessing;
  • 标号5:用一个循环语句,从前一分钟实时行情数据中筛选出当前所监听股票的行情数据,本示例中由两个自定义函数filtering_quotes(onequote_watchingsymbollist)和multiprocess_filteringquotes(quote_watchingsymbol_list),通过多核并行处理来替代
  • 标号7:基于多核处理函数的pool 创建的函数,只能载入长度相等的序列参数(一个为函数名称,另一个为参数),这里要载入两个参数,且两个参数的类型和长度都不一样,因此根据quote数据的长度构建一个等长的列表watching_marketsymbols_2dlist,再合并为一个二维列表,作为一个参数带入multiprocess_filteringquotes函数。

分别以标号5(循环方式)和标号6(多核多进程处理)的方式运行这一段程序,因为标号4 quote数据列表很小,循环方式的耗时反而比多核方式耗时多一点。当quote数据列表的大小到7或8万时,循环方式耗时33秒,多核多进程方式仅为5秒,且多核方式在quote数据列表大小翻倍的情况下,耗时也仅为6-7秒,并没有很大的增加。

系统实际运行表现

在另一台电脑(单芯4核,16G内存)上运行量化交易系统,可监听的股票数量的上限约为400支,能保证系统在40秒内运行一次;在这台用“洋垃圾”服务器处理器的电脑(双芯40核,32G)上运行该系统,可监听的股票数量上限约为3000只,能保证系统在40秒内运行一次。另外,系统中还有不少循环处理没有修改为多核并行处理,如果把其中循环量大的程序段都改成多核并行处理方式,那这一台电脑都可以实现A股全市场监听。这里之所以两台电脑上都要求系统在40秒内运行一次,是因为系统的运行时间周期设置为60秒,为了预防在交易火爆情况下产生的实时行情数量大幅度增加的特殊情况,这种情况下如果不能在58秒以内完成一次完整的运行,那么会实时行情数据来不及处理而堵塞,导致整个系统的运行周期错乱,无法正常运行。

下图是系统运行中多核处理时的cpu占用情况:

python实战 - 多核多进程发挥multiprocessing的强大威力

上图中,系统运行中第一个占用cpu的高峰是多核并行处理实时行情数据并合成多分钟K线数据,第二个占用CPU的高峰是多核并行执行多个交易策略,这个时段更长一点,要完成中证800和中证1000总共1800支股票和多个交易策略比对,以判断某支股票是否满足某个交易策略的执行条件。下图是多核并行处理数据时,各个cpu的占用情况:

python实战 - 多核多进程发挥multiprocessing的强大威力

(本文完)