我們的回測(cè)程序是用Python寫的,因?yàn)槭褂肑upter Notebook顯示結(jié)果非常方便。但是,最近在一次運(yùn)行整個(gè)回測(cè)代碼時(shí),整整花了20分鐘才出現(xiàn)結(jié)果。于是,我們打算好好優(yōu)化一下。最終,性能提升10倍以上,耗時(shí)在1分29秒左右。
優(yōu)化流程
我們優(yōu)化主要分成兩部分,第一部分是程序內(nèi)部邏輯,第二部分是Python提速。所以,我們整個(gè)流程可以分成下面三步:
- 第一步,Python性能檢測(cè)
- 第二步,優(yōu)化程序內(nèi)部邏輯
- 第三步,為Python提速
一、Python性能檢測(cè)
Python性能檢測(cè),我們使用的是line_profiler,它可以檢測(cè)代碼每行消耗的時(shí)間,非常方便好用。
1、line_profiler 安裝
使用pip安裝就行了
$ pip3 install line_profiler
2、line_profiler使用
首先,在需要測(cè)試的函數(shù)中加上修飾器@profile
例如:
@profile
def max_drawdown(data_list):
"""
求最大回撤, 回撤公式=max(Di-Dj)/Di
"""
max_return = 0
for i, v1 in enumerate(data_list):
for j, v2 in enumerate(data_list[i + 1:]):
if v1 == 0:
continue
tmp_value = (v1 - v2) / v1
if tmp_value > max_return:
max_return = tmp_value
return max_return
然后,運(yùn)行腳本,生成.lprof文件
$ kernprof -l lineProfilerDemo.py
最后,查看各行代碼運(yùn)行的時(shí)間
$ python3 -m line_profiler lineProfilerDemo.py.lprof
Timer unit: 1e-06 s
Total time: 0.000475 s
File: lineProfilerDemo.py
Function: max_drawdown at line 4
Line # Hits Time Per Hit % Time Line Contents
==============================================================
4 @profile
5 def max_drawdown(data_list):
6 """
7 求最大回撤, 回撤公式=max(Di-Dj)/Di
8 """
9 1 15.0 15.0 3.2 max_return = 0
10 11 8.0 0.7 1.7 for i, v1 in enumerate(data_list):
11 55 56.0 1.0 11.8 for j, v2 in enumerate(data_list[i + 1:]):
12 45 346.0 7.7 72.8 if v1 == 0:
13 continue
14 45 25.0 0.6 5.3 tmp_value = (v1 - v2) / v1
15 45 23.0 0.5 4.8 if tmp_value > max_return:
16 3 1.0 0.3 0.2 max_return = tmp_value
17 1 1.0 1.0 0.2 return max_return
最后% Time那一列就是本行代碼消耗的時(shí)間占比。
注意:每次只能檢測(cè)一個(gè)函數(shù),如果檢測(cè)某一行代碼執(zhí)行比較慢??梢詫profile移動(dòng)到這一行代碼對(duì)應(yīng)的方法,然后繼續(xù)往內(nèi)部檢測(cè)。
3、在Jupyter Notebook中使用
先使用%load_ext line_profiler加載,然后使用lprun -s -f語句打印結(jié)果,具體使用如下:
%load_ext line_profiler
import numpy as np
def max_drawdown(data_list):
"""
求最大回撤, 回撤公式=max(Di-Dj)/Di
"""
max_return = 0
for i, v1 in enumerate(data_list):
for j, v2 in enumerate(data_list[i + 1:]):
if v1 == 0:
continue
tmp_value = (v1 - v2) / v1
if tmp_value > max_return:
max_return = tmp_value
return max_return
value_array = np.random.randint(1, high=10, size=10)
%lprun -s -f max_drawdown max_drawdown(value_array)
效果如下:

二、優(yōu)化程序內(nèi)部邏輯
我們的回測(cè)代碼,主要分為數(shù)據(jù)讀取、數(shù)據(jù)處理、交易邏輯、回測(cè)結(jié)果顯示四部分。其中,我們主要優(yōu)化了三方面的代碼:
- 優(yōu)化數(shù)據(jù)讀取性能
- 簡(jiǎn)化數(shù)據(jù)
- 改進(jìn)算法
1、優(yōu)化數(shù)據(jù)讀取性能
最初,我們讀取數(shù)據(jù)是這樣的:

最終,優(yōu)化之后讀取數(shù)據(jù)過程是這樣的:

具體流程如下:
第一次回測(cè)的時(shí)候,我們會(huì)到局域網(wǎng)的Mongodb上讀取數(shù)據(jù),同時(shí)緩存一份json文件在本地。并且,把對(duì)應(yīng)的數(shù)據(jù)緩存一份在Jupyter notebook的內(nèi)存里面。
那么,當(dāng)下一次回測(cè)的時(shí)候:
- 如果Jupter notebook未重啟,我們直接從內(nèi)存中拿取數(shù)據(jù),這個(gè)就不用耗費(fèi)時(shí)間重新讀取
- 如果Jupter notebook重啟了,我們就從本地json文件中讀取數(shù)據(jù),這個(gè)也會(huì)比從局域網(wǎng)Mongodb中讀取快很多
- 如果,json文件有24小時(shí)以上沒有更新了,我們則再次從局域網(wǎng)Mongodb中讀取,并更新本地緩存
2、簡(jiǎn)化數(shù)據(jù)
在檢測(cè)處理數(shù)據(jù)的時(shí)候,發(fā)現(xiàn)計(jì)算天的指標(biāo)時(shí)間基本可以忽略,最主要的時(shí)間放在遍歷分鐘數(shù)據(jù)的for循環(huán)里面。其實(shí),這個(gè)for循環(huán)代碼特別簡(jiǎn)單,但是因?yàn)榉昼姅?shù)據(jù)有超過9百萬條,導(dǎo)致處理它成為了主要花費(fèi)時(shí)間。那我們是否可以簡(jiǎn)化數(shù)據(jù),優(yōu)化這個(gè)性能?
我們的分鐘數(shù)據(jù)主要有:high、low、open、close、volume、timestamp、date_time、frame、symbol等數(shù)據(jù),但是我們暫時(shí)用到的只有high、low、open、close、timestamp,于是最終我們只保留了6個(gè)字段high、low、open、close、timestamp、volume,volume怕以后可能會(huì)用到。
同時(shí),除了減少字段以為,我們將本來存儲(chǔ)是字典的數(shù)據(jù)結(jié)構(gòu),改成數(shù)組,以open、close、high、low、volume、timestamp的固定順序存儲(chǔ)。
這是因?yàn)?,我們?cè)跍y(cè)試性能的時(shí)候發(fā)現(xiàn),數(shù)組不管是讀取數(shù)據(jù)還是遍歷,都比字典快,測(cè)試代碼如下:
import time
def test_dic1(info):
a, b = info['name'], info['height']
def test_dic2(info):
for key in info:
pass
def test_array1(info):
a, b = info[0], info[1]
def test_array2(info):
for value in info:
pass
start_timestamp = time.time()
for i in range(10**6):
test_array1(['Jack', 1.8, 1])
print("取元素?cái)?shù)組消耗時(shí)間: {:.4f}秒".format(time.time() - start_timestamp))
start_timestamp = time.time()
for i in range(10**6):
test_dic1({
'name': 'Jack',
'height': 1.8,
'sex': 1
})
print("取元素字典消耗時(shí)間: {:.4f}秒\n".format(time.time() - start_timestamp))
start_timestamp = time.time()
for i in range(10**6):
test_array2(['Jack', 1.8, 1])
print("遍歷數(shù)組消耗時(shí)間: {:.4f}秒".format(time.time() - start_timestamp))
start_timestamp = time.time()
for i in range(10**6):
test_dic2({
'name': 'Jack',
'height': 1.8,
'sex': 1
})
print("遍歷字典消耗時(shí)間: {:.4f}秒".format(time.time() - start_timestamp))
結(jié)果:
$ python3 testDicAndArray.py
取元素?cái)?shù)組消耗時(shí)間: 0.2128秒
取元素字典消耗時(shí)間: 0.2779秒
遍歷數(shù)組消耗時(shí)間: 0.2346秒
遍歷字典消耗時(shí)間: 0.2985秒
比較之后,發(fā)現(xiàn)數(shù)組的速度比字典快了20%以上。
最終,在稍微犧牲了一點(diǎn)可讀性,簡(jiǎn)化了數(shù)據(jù)之后,我們的性能提升了很大,主要有三方面:
- 本地的json文件體積變成了以前的1/3,讀取速度提升了將近80%
- 處理數(shù)據(jù)性能得到提升
- 交易邏輯性能得到提升
3、改進(jìn)算法
(1)for循環(huán)內(nèi)部數(shù)據(jù)提取到循環(huán)外面
通過line_profile檢測(cè),我發(fā)現(xiàn)有個(gè)判斷語句時(shí)間占比比較大,代碼邏輯大概如下所示:
for i in range(10000000):
if i < self.every_coin_max_cash_unit() and (self.max_total_cash_unit() == -1 or total_cash_unit <= self.max_total_cash_unit()):
pass
然后分析之后,我發(fā)現(xiàn)every_coin_max_cash_unit()方法和max_total_cash_unit()方法的值只是2個(gè)配置參數(shù),配置好了就不會(huì)再變化。
于是,使用兩個(gè)變量緩存下,放到for循環(huán)外面,優(yōu)化成了下面這樣:
every_coin_max_cash_unit = self.every_coin_max_cash_unit()
max_total_cash_unit = self.max_total_cash_unit()
# 當(dāng)為-1時(shí), 給1個(gè)很大值,大于1000就夠了
max_total_cash_unit = 100000 if max_total_cash_unit == -1 else max_total_cash_unit
for i in range(10000000):
if i < every_coin_max_cash_unit and total_cash_unit <= max_total_cash_unit:
pass
像這種優(yōu)化,至少優(yōu)化了3處。例如,在for循環(huán)內(nèi)部獲取數(shù)組長(zhǎng)度等等。
(2)最大回撤,將O(n2)算法優(yōu)化成O(n)
在優(yōu)化完測(cè)試的數(shù)據(jù)后,我們嘗試跑整個(gè)數(shù)據(jù),發(fā)現(xiàn)回測(cè)顯示結(jié)果特別慢。然后使用line_profile去檢測(cè),一層一層往里面查的時(shí)候,發(fā)現(xiàn)計(jì)算最大回撤的時(shí)候特別耗時(shí)。下面是以前計(jì)算回撤的代碼:
def max_drawdown(data_list):
"""
求最大回撤, 回撤公式=max(Di-Dj)/Di
"""
max_return = 0
for i, v1 in enumerate(data_list):
for j, v2 in enumerate(data_list[i + 1:]):
if v1 == 0:
continue
tmp_value = (v1 - v2) / v1
if tmp_value > max_return:
max_return = tmp_value
return max_return
它的時(shí)間復(fù)雜度是O(n2),而收益數(shù)據(jù)有28000多條(每小時(shí)記錄一次),那計(jì)算數(shù)據(jù)量達(dá)到了8.4億次,這樣就比較大了。
參考了【Python量化】O(n)復(fù)雜度實(shí)現(xiàn)最大回撤的計(jì)算的思路后,我們將代碼優(yōu)化成O(n),代碼示例如下:
def new_max_drawdown(data_array):
"""
求最大回撤
:param array:
:return:
"""
drawdown_array = []
max_value = data_array[0]
for value in data_array:
# 當(dāng)前值超過最大值, 則替換值, 并且當(dāng)前是波峰, 所以當(dāng)前值對(duì)應(yīng)最大回撤為0
if value > max_value:
max_value = value
drawdown_array.append(0)
else:
drawdown_value = (max_value - value) / max_value
drawdown_array.append(drawdown_value)
return max(drawdown_array)
性能,又得到提升。
(3)緩存好計(jì)算結(jié)果,直接傳遞參數(shù)
這方面,和第一點(diǎn)道理有點(diǎn)類似,這里主要有兩個(gè)例子:
- 我們的回測(cè)結(jié)果是比較豐富的,不單單會(huì)計(jì)算每年的收益率、夏普值、索提諾值等等,還會(huì)精確計(jì)算到每個(gè)月。雖然,最大回撤的算法優(yōu)化了,這方面得到很大的提升,但是我們覺得還不是極限。在使用line_profile檢測(cè)之后,發(fā)現(xiàn)計(jì)算每個(gè)月結(jié)果花費(fèi)的時(shí)間占比比較大。分析之后,才知道每個(gè)月都會(huì)重新再計(jì)算一下市值、收益率等等,這個(gè)其實(shí)完全可以在最外面計(jì)算好,然后取某一段數(shù)據(jù),當(dāng)做參數(shù)傳遞過去。
- 我們回測(cè)代碼剛開始在回測(cè)數(shù)據(jù)的時(shí)候,是分成每年、2013至2019、2016至2018、2018至2019多個(gè)時(shí)間段回測(cè),然后每回測(cè)一次,都會(huì)重新再處理一遍數(shù)據(jù)。這種重復(fù)計(jì)算,造成了好十幾秒的浪費(fèi)。于是,我們將整體代碼邏輯改一下,先一次性將回測(cè)數(shù)據(jù)處理好,然后再分別取出來使用,不用再重新處理。
三、為Python提速
Python語言提速,我們分別嘗試了三種方案:
- Cython
- Pypy
- Numba
1、Cython
Cython將Python代碼翻譯成C語言版本,然后生成Python擴(kuò)展模塊,供我們使用。
Cython安裝很簡(jiǎn)單,pip3 install Cython就行了。
怎么使用呢?
例如我們有一個(gè)Python腳本test_array.py,內(nèi)容如下:
def test_array(info):
for i in range(100):
sum = 0
for value in info:
if value % 2 == 0:
sum += value
else:
sum -= value
我們?cè)?code>test.py腳本中引入test_array模塊,代碼如下:
import time
from test_array import test_array
start_timestamp = time.time()
test_array(range(1, 100000))
print("消耗時(shí)間: {:.4f}秒".format(time.time() - start_timestamp))
我們執(zhí)行test.py腳本,獲得了下面結(jié)果:
$ python3 test.py
消耗時(shí)間: 1.0676秒
下面,我們使用Cython為它提速。
首先,我們將test_array.py中的內(nèi)容copy到另一個(gè)文件test_array1.pyx中。
然后,我們創(chuàng)建一個(gè)setup.py文件,它的內(nèi)容如下:
from distutils.core import setup
from Cython.Build import cythonize
setup(
name='test_array',
ext_modules=cythonize('test_array1.pyx'),
)
之后,我們執(zhí)行下面語句編譯腳本
python3 setup.py build_ext --inplace
這一步,它為我們生成了test_array1.c和test_array1.cpython-37m-darwin.so文件
接著一步,我們?cè)?code>test.py中導(dǎo)入test_array1模塊
import time
# from test_array import test_array
from test_array1 import test_array
start_timestamp = time.time()
test_array(range(1, 100000))
print("消耗時(shí)間: {:.4f}秒".format(time.time() - start_timestamp))
最后,我們執(zhí)行test.py
$ python3 test.py
消耗時(shí)間: 0.4671秒
什么代碼都沒有改,我們就為Python提升2倍多的速度。
不過,根據(jù)cython建議,我們?cè)诖_定數(shù)據(jù)類型情況下,可以使用cdef先確定數(shù)據(jù)類型,test_array1.pyx修改后代碼如下:
cpdef test_array(info):
cdef int i, sum, value
for i in range(100):
sum = 0
for value in info:
if value % 2 == 0:
sum += value
else:
sum -= value
執(zhí)行test.py
$ python3 test.py
消耗時(shí)間: 0.1861秒
又提升了2倍的速度,相比于原生的python代碼,提升了將近6倍的速度。
其實(shí),若是我們傳遞的數(shù)組確定類型,那么速度還能提升,test_array1.pyx代碼修改如下:
cpdef test_array(int[:] info):
cdef int i, sum, value, j
cdef int n = len(info)
for i in range(100):
sum = 0
for j in range(n):
value = info[j]
if value % 2 == 0:
sum += value
else:
sum -= value
此時(shí),我們test.py也需要修改下,傳遞的數(shù)據(jù)是array類型,如下:
import time
# from test_array import test_array
from test_array1 import test_array
import array
start_timestamp = time.time()
a_array = array.array('i', range(1, 100000))
test_array(a_array)
# test_array(range(1, 100000))
print("消耗時(shí)間: {:.4f}秒".format(time.time() - start_timestamp))
最后,執(zhí)行test.py,看下效果
$ python3 test.py
消耗時(shí)間: 0.0138秒
相比于前面代碼,提升了10多倍,比原生的python代碼,提升了75倍。
總結(jié):Cython完全兼容Python,也支持對(duì)C語言代碼的支持。不過,它對(duì)代碼優(yōu)化方面,需要下一些功夫才能達(dá)到極致效果。
2、Pypy
Pypy是Python語言的JIT編譯器,它的運(yùn)行速度在某些方面比原生更快。
安裝Pypy3
$ brew install pypy3
?? /usr/local/Cellar/pypy3/7.0.0: 5,776 files, 135.5MB
下面,我們?cè)囋噋ypy的速度,執(zhí)行前面test.py的原生python腳本,內(nèi)容如下:
import time
from test_array import test_array
start_timestamp = time.time()
test_array(range(1, 100000))
print("消耗時(shí)間: {:.4f}秒".format(time.time() - start_timestamp))
結(jié)果如下:
$ pypy3 test.py
消耗時(shí)間: 0.0191秒
不需要任何代碼修改,它將近達(dá)到我們上面Cython最終效果,比原生的Python快54倍。
由于它是Python的另外一套編譯器了,需要重新安裝依賴環(huán)境,下面是我們安裝過程遇到的一些問題:
(1)pypy安裝numpy后,運(yùn)行報(bào)錯(cuò),錯(cuò)誤信息:
Traceback (most recent call last):
File "/usr/local/Cellar/pypy3/7.0.0/libexec/site-packages/numpy/core/init.py", line 40, in <module>
from . import multiarray
File "/usr/local/Cellar/pypy3/7.0.0/libexec/site-packages/numpy/core/multiarray.py", line 12, in <module>
from . import overrides
File "/usr/local/Cellar/pypy3/7.0.0/libexec/site-packages/numpy/core/overrides.py", line 6, in <module>
from numpy.core._multiarray_umath import (
ImportError: dlopen(/usr/local/Cellar/pypy3/7.0.0/libexec/site-packages/numpy/core/_multiarray_umath.pypy3-70-darwin.so, 6): Symbol not found: _PyStructSequence_InitType2
Referenced from: /usr/local/Cellar/pypy3/7.0.0/libexec/site-packages/numpy/core/_multiarray_umath.pypy3-70-darwin.so
Expected in: dynamic lookup
后來發(fā)現(xiàn)豆瓣源過時(shí)了,于是換成官方的源,重新安裝就行了:
$ vim ~/.pip/pip.conf
[global]
#index-url = https://pypi.douban.com/simple/
#index-url = http://mirrors.aliyun.com/pypi/simple/
index-url = https://pypi.python.org/pypi
換回官方源,執(zhí)行pip_pypy3 install numpy,安裝的的numpy版本是numpy-1.16.4,果然就沒問題了。豆瓣源還是numpy-1.16.3
(2)沒有安裝模塊playhouse,具體信息:
Traceback (most recent call last):
File "BackTestFunction.py", line 1, in <module>
from utils import helper
File "/Users/liuchungui/Sites/python/BackTest/utils/helper.py", line 3, in <module>
from playhouse.shortcuts import model_to_dict
ModuleNotFoundError: No module named 'playhouse'
原來是因?yàn)闆]有安裝peewee這個(gè)庫(kù)導(dǎo)致的,重新安裝:pip_pypy3 install peewee
(3)安裝pandas失敗,報(bào)錯(cuò)信息如下:
Downloading https://files.pythonhosted.org/packages/b2/4c/b6f966ac91c5670ba4ef0b0b5613b5379e3c7abdfad4e7b89a87d73bae13/pandas-0.24.2.tar.gz (11.8MB)
100% |████████████████████████████████| 11.8MB 401kB/s
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/private/var/folders/qc/z1r6s9rs3h7dtlthq1dstq840000gn/T/pip-install-9j2r7tk5/pandas/setup.py", line 438, in <module>
if python_target < '10.9' and current_system >= '10.9':
File "/usr/local/Cellar/pypy3/7.0.0/libexec/lib-python/3/distutils/version.py", line 52, in __lt__
c = self._cmp(other)
File "/usr/local/Cellar/pypy3/7.0.0/libexec/lib-python/3/distutils/version.py", line 335, in _cmp
if self.version == other.version:
AttributeError: 'LooseVersion' object has no attribute 'version'
----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /private/var/folders/qc/z1r6s9rs3h7dtlthq1dstq840000gn/T/pip-install-9j2r7tk5/pandas/
原來是安裝最新版本會(huì)報(bào)錯(cuò),所以安裝0.23.4,命令如下:
$ pip_pypy3 install pandas==0.23.4
參考:centos7 pypy python3.6版本安裝編譯及numpy,pandas安裝
(4)安裝talib
pip_pypy3 install TA-Lib
(5)Pypy在Jupter Notebook使用
只需要改下kernel就行了,操作如下:
復(fù)制一份kernel,命名為pypy3
cp ~/Library/Jupyter/kernels/python3/ ~/Library/Jupyter/kernels/pypy3
編輯kernel.json,使用pypy3
$ vim kernel.json
{
"argv": [
"/usr/local/bin/pypy3",
"-m",
"ipykernel_launcher",
"-f",
"{connection_file}"
],
"display_name": "pypy3",
"language": "python"
}
保存之后,我們?cè)贘upter Notebook選擇kernel時(shí),選擇pypy3就行了。
總結(jié):Pypy不需要修改代碼就能運(yùn)行并得到加速,這方面很方便。不過,它也有缺點(diǎn),它不兼容Cpython,在科學(xué)計(jì)算領(lǐng)域中兼容性也不行,有時(shí)候性能反而更差。例如,我曾經(jīng)準(zhǔn)備優(yōu)化讀取文件,默認(rèn)情況下一個(gè)一個(gè)讀取文件需要花費(fèi)12秒;使用多線程沒有效果(因?yàn)镚IL,其實(shí)還是一個(gè)線程);使用多進(jìn)程只需要花費(fèi)4.4秒,提升效果明顯;使用多進(jìn)程+Pypy,檢查了進(jìn)程,根本沒有用到多進(jìn)程,而速度卻反而變慢了。還有,在使用ujson解析讀取json文件時(shí),速度也反而慢了。
3、Numba
Numba是一個(gè)JIT編譯器,可以在運(yùn)行時(shí)將Python代碼編譯成機(jī)器代碼,和Pypy類似,不過它對(duì)CPython兼容性更好。
不過,不同于Pypy,它是用裝飾器@jit()來為指定函數(shù)加速。
test_array.py代碼如下:
from numba import jit
@jit()
def test_array(info):
for i in range(100):
sum = 0
for value in info:
if value % 2 == 0:
sum += value
else:
sum -= value
test.py傳遞numpy數(shù)組參數(shù),如下:
import time
from test_array import test_array
import numpy as np
start_timestamp = time.time()
a = np.array(range(1, 100000))
test_array(a)
print("消耗時(shí)間: {:.4f}秒".format(time.time() - start_timestamp))
執(zhí)行結(jié)果如下:
$ python3 test.py
消耗時(shí)間: 0.2292秒
而若是傳遞Numpy參數(shù)數(shù)組,不使用Numba加速,耗時(shí)是6.6148,所以它為Numpy加速將近30倍。
不過,Numba對(duì)原生的list不會(huì)加速,反而會(huì)讓代碼運(yùn)行更慢。例如,上面test.py代碼如下:
import time
from test_array import test_array
start_timestamp = time.time()
test_array(range(1, 100000))
print("消耗時(shí)間: {:.4f}秒".format(time.time() - start_timestamp))
執(zhí)行結(jié)果如下:
$ python3 test.py
消耗時(shí)間: 1.6948秒
總結(jié):Numba使用@jit()裝飾器來進(jìn)行加速,它可以指定函數(shù)進(jìn)行加速,但是不能整體加速,使用起來沒有Pypy方便,而且對(duì)普通的Python代碼沒有加速效果。不過,它的優(yōu)勢(shì)在于支持Numpy等科學(xué)計(jì)算領(lǐng)域的框架,對(duì)Numpy有更一層的加速效果。
總結(jié)
最終我們選擇了使用Pypy,理由如下:
1、Pypy不用我們修改任何代碼,不像Cython那樣麻煩,比Numba也方便
2、Pypy對(duì)純Python代碼加速效果很好,我們基本上都是純Python代碼,而且整體加速效果很明顯
3、Pypy缺點(diǎn)是數(shù)據(jù)讀取比原生更慢,而且不能使用ujson這種C語言框架來加速。不過在簡(jiǎn)化數(shù)據(jù)之后,暫時(shí)還能接受。而且,后期可以考慮通過Pypy的腳本調(diào)用Python腳本,然后獲取數(shù)據(jù)來進(jìn)行提速。
參考
優(yōu)化 Python 性能:PyPy、Numba 與 Cython,誰才是目前最優(yōu)秀的 Python 運(yùn)算解決方案?
干貨 | Python 性能優(yōu)化的20條招數(shù)