Python相比于C++来说有着十分用户友好的编程方式与众多的机器学习和深度学习库,入门快、学习轻松,但其性能劣势一直为人诟病。因此,很多工程师致力于提高python代码性能。本文记录下目前我所知道的Python代码加速方法!

Numba

Numba 是一个开源的 JIT 编译器可以纯Python和Numpy代码转为快速地机器码执行,但其不能加速Pandas代码而且在多线程的任务出容易出错!

numba可以将python变为C的性能,并且所有代码的改动点只需要加一行代码!

参考官网地址:https://numba.pydata.org/

下面以官方计算π\pi的例子来测试性能提升幅度:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from numba import jit
import random
import time

def monte_carlo_pi(nsamples):
acc = 0
for i in range(nsamples):
x = random.random()
y = random.random()
if (x ** 2 + y ** 2) < 1.0:
acc += 1
return 4.0 * acc / nsamples


@jit(nopython=True)
def monte_carlo_pi_numba(nsamples):
acc = 0
for i in range(nsamples):
x = random.random()
y = random.random()
if (x ** 2 + y ** 2) < 1.0:
acc += 1
return 4.0 * acc / nsamples

def test_time(func, *args, **kwargs):
start = time.time()
res = func(*args)
end = time.time()
return end-start


magnitude1 = 10000
print(f"Magnitude {magnitude1} : non-accelerated costed time:{test_time(monte_carlo_pi, magnitude1)}")
print(f"Magnitude {magnitude1} : accelerated costed time:{test_time(monte_carlo_pi_numba, magnitude1)}")

magnitude2 = 100000
print(f"Magnitude {magnitude2} : non-accelerated costed time:{test_time(monte_carlo_pi, magnitude2)}")
print(f"Magnitude {magnitude2} : accelerated costed time:{test_time(monte_carlo_pi_numba, magnitude2)}")
Magnitude 10000 : non-accelerated costed time:0.004992485046386719
Magnitude 10000 : accelerated costed time:0.2682771682739258
Magnitude 100000 : non-accelerated costed time:0.053856611251831055
Magnitude 100000 : accelerated costed time:0.0010225772857666016

从上述时间测试中可以看出,对于量级小于100000的计算量来使用Numba加速发现加速效果并不佳,而且还可能减慢运行时间!因此需要根据自己的数据量来选择是否使用Numba!

上面谈到Numba仅能加速纯python操作和numpy操作,那么对于数据分析中另一常用库 pandas 如何加速呢?写下自己了解的两种方法。

Python 多线程

python多线程可以加速任意的操作,加速的上限取决于电脑的性能,可以使用python多线程并行处理来加速pandas操作。操作代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np
import pandas as pd
from multiprocessing import cpu_count, Pool


def parallelize(func, df):
""" Split data into max core partitions and execute func in parallel.
https://www.machinelearningplus.com/python/parallel-processing-python/
Parameters
----------
df : pandas Dataframe
func : any functions
Returns
-------
data : pandas Dataframe
Returned dataframe of func.
"""
cores = cpu_count()
data_split = np.array_split(df, cores)
pool = Pool(cores)
data = pd.concat(pool.map(func, data_split), ignore_index=1)
pool.close()
pool.join()
return data

测试多线程处理的性能:

1
2
3
4
5
6
7
8
9
10
nsamples = 100
data = pd.DataFrame({"a": np.random.rand(nsamples), "b": np.random.rand(nsamples)})

def func(data):
"""test func."""
return data["a"].apply(lambda x: np.sqrt(x)) - data["b"] ** 2

if __name__ == '__main__':
print(f"non-accelerated costed time:{test_time(func, data)}")
print(f"accelerated costed time:{test_time(parallelize, func, data)}")

注意:在Windows上要想使用multipropressing模块,必须把有关进程的代码写 if __name__ == ‘__main__’ 语句中,才能正常使用Windows下的进程模块,否则进程将不会停止。Unix/Linux下则不需要。 这个Bug真是让我头疼半天!

Modin 库

Modin 库是pandas库的升级版,同样是使用多核调度的方法来加速pandas中的操作,和numba一样操作简单,它只需增加一行代码来优化而不需要自己控制调度过程!

一行代码加速确实非常香,但缺点是需要安装底层依赖rayray 或者daskdask,此外也不是完全支持pandas操作,待进一步完善。可以满足日常需求!参考: modin-project/modin

给出官方的测试效果,其它细节可以参考官方文档:文档说明

目前暂时用不到这库,因此以后有需求的时候再补充使用说明吧!

联系作者