Python 对于纯numpy代码,使用numba的收益来自哪里?
我想了解在for循环中使用Numba来加速purePython 对于纯numpy代码,使用numba的收益来自哪里?,python,numpy,numba,Python,Numpy,Numba,我想了解在for循环中使用Numba来加速purenumpy代码时,收益来自哪里。是否有任何分析工具允许您查看jitted函数 演示代码(如下)只是使用非常基本的矩阵乘法为计算机提供工作。观察到的收益来自: 更快的循环 编译过程中被jit截获的numpy函数的重铸,或 使用jit减少开销,因为numpy通过包装函数将执行外包给低级库,如LINPACK %matplotlib内联 将numpy作为np导入 从numba导入jit 作为pd进口熊猫 #矩阵的维数 i=100 j=100 def pu
numpy
代码时,收益来自哪里。是否有任何分析工具允许您查看jitted
函数
演示代码(如下)只是使用非常基本的矩阵乘法为计算机提供工作。观察到的收益来自:
循环
jit
截获的numpy
函数的重铸,或jit
减少开销,因为numpy通过包装函数将执行外包给低级库,如LINPACK
%matplotlib内联
将numpy作为np导入
从numba导入jit
作为pd进口熊猫
#矩阵的维数
i=100
j=100
def pure_python(N,i,j):
对于范围内的n(n):
a=np.random.rand(i,j)
b=np.random.rand(i,j)
c=np.点(a,b)
@jit(nopython=True)
def jit_python(N,i,j):
对于范围内的n(n):
a=np.random.rand(i,j)
b=np.random.rand(i,j)
c=np.点(a,b)
时间_python=[]
时间(jit)=[]
N=[1,1010050010002000]
对于n中的n:
time=%timeit-oq纯python(n,i,j)
time\u python.append(time.average)
time=%timeit-oq jit_python(n,i,j)
time\u jit.append(time.average)
数据帧({'pure_python':time_python'jit_python':time_jit},index=N)
df.index.name='Iterations'
df[[“pure_python”,“jit_python”]].plot()
生成以下图表
TL:DR随机循环和循环加速,但矩阵乘法除了较小的矩阵大小外,不会发生。在较小的矩阵/循环大小下,似乎存在可能与python开销有关的显著加速。在很大程度上,矩阵乘法开始占主导地位,而jit则没有什么帮助 函数定义,为简单起见,使用平方矩阵
from IPython.display import display
import numpy as np
from numba import jit
import pandas as pd
#Dimensions of Matrices
N = 1000
def py_rand(i, j):
a = np.random.rand(i, j)
jit_rand = jit(nopython=True)(py_rand)
def py_matmul(a, b):
c = np.dot(a, b)
jit_matmul = jit(nopython=True)(py_matmul)
def py_loop(N, val):
count = 0
for i in range(N):
count += val
jit_loop = jit(nopython=True)(py_loop)
def pure_python(N,i,j):
for n in range(N):
a = np.random.rand(i,j)
b = np.random.rand(i,j)
c = np.dot(a,a)
jit_func = jit(nopython=True)(pure_python)
时间:
df = pd.DataFrame(columns=['Func', 'jit', 'N', 'Time'])
def meantime(f, *args, **kwargs):
t = %timeit -oq -n5 f(*args, **kwargs)
return t.average
for N in [10, 100, 1000, 2000]:
a = np.random.randn(N, N)
b = np.random.randn(N, N)
df = df.append({'Func': 'jit_rand', 'N': N, 'Time': meantime(jit_rand, N, N)}, ignore_index=True)
df = df.append({'Func': 'py_rand', 'N': N, 'Time': meantime(py_rand, N, N)}, ignore_index=True)
df = df.append({'Func': 'jit_matmul', 'N': N, 'Time': meantime(jit_matmul, a, b)}, ignore_index=True)
df = df.append({'Func': 'py_matmul', 'N': N, 'Time': meantime(py_matmul, a, b)}, ignore_index=True)
df = df.append({'Func': 'jit_loop', 'N': N, 'Time': meantime(jit_loop, N, 2.0)}, ignore_index=True)
df = df.append({'Func': 'py_loop', 'N': N, 'Time': meantime(py_loop, N, 2.0)}, ignore_index=True)
df = df.append({'Func': 'jit_func', 'N': N, 'Time': meantime(jit_func, 5, N, N)}, ignore_index=True)
df = df.append({'Func': 'py_func', 'N': N, 'Time': meantime(pure_python, 5, N, N)}, ignore_index=True)
df['jit'] = df['Func'].str.contains('jit')
df['Func'] = df['Func'].apply(lambda s: s.split('_')[1])
df.set_index('Func')
display(df)
结果:
Func jit N Time
0 rand True 10 1.030686e-06
1 rand False 10 1.115149e-05
2 matmul True 10 2.250371e-06
3 matmul False 10 2.199343e-06
4 loop True 10 2.706000e-07
5 loop False 10 7.274286e-07
6 func True 10 1.217046e-05
7 func False 10 2.495837e-05
8 rand True 100 5.199217e-05
9 rand False 100 8.149794e-05
10 matmul True 100 7.848071e-05
11 matmul False 100 2.130794e-05
12 loop True 100 2.728571e-07
13 loop False 100 3.003743e-06
14 func True 100 6.739634e-04
15 func False 100 1.146594e-03
16 rand True 1000 5.644258e-03
17 rand False 1000 8.012790e-03
18 matmul True 1000 1.476098e-02
19 matmul False 1000 1.613211e-02
20 loop True 1000 2.846572e-07
21 loop False 1000 3.539849e-05
22 func True 1000 1.256926e-01
23 func False 1000 1.581177e-01
24 rand True 2000 2.061612e-02
25 rand False 2000 3.204709e-02
26 matmul True 2000 9.866484e-02
27 matmul False 2000 1.007234e-01
28 loop True 2000 3.011143e-07
29 loop False 2000 7.477454e-05
30 func True 2000 1.033560e+00
31 func False 2000 1.199969e+00
看起来numba在优化循环,所以我不想麻烦把它包括在比较中
绘图:
因此,对于5次重复的循环,jit将大大加快速度,直到矩阵乘法变得足够昂贵,使得其他开销相比之下变得微不足道 我想Numba可以识别
np.random.rand
和np.dot
。(如果没有,我认为它不会让你在nopython模式下使用它们。)确实如此。它们由文档中的numba
支持。我主要想知道代码拦截是如何工作的,以及在上面的例子中这是否是收益的来源。你能添加一些设置信息吗?在Win64、Python3.5、numba 0.33上,我肯定只有适度的加速(10-15%)。我使用的是Linux Mint 18、Linux内核4.4.0-45-generic、python 3.5、numba 0.30.1、Intel Xeon CPU E5-1620@3.6Ghz x 4据我所知,答案是1)和2)numba
将函数编译为c
code。因此,它大大加快了循环解析的速度,并加快了具有显著python
开销的numpy
函数的速度(通常通过剥离该开销并强制执行显式数据排序-即大多数构造函数上的noaxis
关键字、noeinsum
、nosize
参数)(random.rand
是一个例外)…所有这些事情都可以在现在更快的for
循环中明确完成)您可能有兴趣修复def py_循环():
代码,因为原来的代码主要是一个O(1)
缩放的(从概念上讲,可能是一个被忽略的快捷方式/屏蔽变量错误,与N
)无关,它扭曲了实验,总是返回~163-294[ns]
处理持续时间(只确认“常量”)普通调用签名处理开销+JMP/RET持续时间,而不是任何N
-次循环执行代码)。不,您必须返回一个值-一个“there”-生成的值,否则JIT编译器分析看不到通过“silicon silicon”多次循环的任何实际原因-代码,如果它不返回任何内容…最好更仔细地设计numba.jit(…)
测试有效负载,否则您再次求助于不测试O/P认为合理的任何内容。我最初关心的是这一点,但由于没有任何函数具有恒定的时间缩放(没有任何函数返回)我认为numba jit实际上并没有完全优化代码。我可能错了。我不想把计时与返回、类型转换等混为一谈。
def jit_speedup(d):
py_time = d[d['jit'] == False]['Time'].mean()
jit_time = d[d['jit'] == True]['Time'].mean()
return py_time / jit_time
import seaborn as sns
result = df.groupby(['Func', 'N']).apply(jit_speedup).reset_index().rename(columns={0: 'Jit Speedup'})
result = result[result['Func'] != 'loop']
sns.factorplot(data=result, x='N', y='Jit Speedup', hue='Func')