Python 在Cython与NumPy中求整数与浮点之和时,性能差异较大
我使用Cython或NumPy对一维数组中的每个元素求和。对整数求和时Cython的速度约为20%。当求和浮动时,Cython的速度约为的2.5倍。下面是使用的两个简单函数Python 在Cython与NumPy中求整数与浮点之和时,性能差异较大,python,numpy,cython,Python,Numpy,Cython,我使用Cython或NumPy对一维数组中的每个元素求和。对整数求和时Cython的速度约为20%。当求和浮动时,Cython的速度约为的2.5倍。下面是使用的两个简单函数 #cython: boundscheck=False #cython: wraparound=False def sum_int(ndarray[np.int64_t] a): cdef: Py_ssize_t i, n = len(a) np.int64_t total = 0
#cython: boundscheck=False
#cython: wraparound=False
def sum_int(ndarray[np.int64_t] a):
cdef:
Py_ssize_t i, n = len(a)
np.int64_t total = 0
for i in range(n):
total += a[i]
return total
def sum_float(ndarray[np.float64_t] a):
cdef:
Py_ssize_t i, n = len(a)
np.float64_t total = 0
for i in range(n):
total += a[i]
return total
时间安排
创建两个阵列,每个阵列包含100万个元素:
a_int = np.random.randint(0, 100, 10**6)
a_float = np.random.rand(10**6)
%timeit sum_int(a_int)
394 µs ± 30 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit a_int.sum()
490 µs ± 34.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit sum_float(a_float)
982 µs ± 10.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit a_float.sum()
383 µs ± 4.42 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
附加点
- NumPy在浮动方面表现出色(相当大的幅度),甚至超过了它自己的整数和
的性能差异与缺少的sum_float
和boundscheck
指令相同。为什么?wrapparound
- 将
中的整数numpy数组转换为C指针(sum\u int
)可将性能提高25%。这样做对浮动没有任何作用np.int64\u t*arr=a.data
def count_int():
cdef:
Py_ssize_t i, n = 1000000
int ct=0
for i in range(n):
ct += 1
return ct
def count_double():
cdef:
Py_ssize_t i, n = 1000000
double ct=0
for i in range(n):
ct += 1
return ct
计算时间
我只运行了一次(害怕缓存)。不知道循环是否实际针对整数执行,但是count\u double
的性能与上面的sum\u float
相同。这太疯狂了
%timeit -n 1 -r 1 count_int()
1.1 µs ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
%timeit -n 1 -r 1 count_double()
971 µs ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
我不会回答你所有的问题,但只回答(在我看来)最有趣的问题 让我们从您的计数示例开始:
1.0*10**6
,并且因为cython按照默认值以(非-ffast math
)模式编译>>> sum_float(a_float)-a_float.sum()
2.9103830456733704e-08
是的,没有人告诉numpy(与你的cython代码不同)总和必须这样计算
((((a_1+a2)+a3)+a4)+...
numpy通过两种方式利用它:
pairwise\u sum\u DOUBLE
)a2+a10
的结果不依赖于a1+a9
,这两个值可以在现代CPU上同时计算(例如),这会导致您观察到的速度加快
值得一提的是,在我的机器上,cython整数和比numpy的慢 需要考虑numpy阵列的跨步(这仅在运行时已知,另请参见关于矢量化)会阻止某些优化。一种解决方法是使用内存视图,对于该视图,您可以明确数据是连续的,即:
def sum_int_cont(np.int64_t[::1] a):
这将导致我的机器显著加速(系数2):
的确,在这种情况下,对double使用内存视图不会带来任何速度提升(不知道为什么),但总体而言,它简化了优化器的使用寿命。例如,将内存视图变量与-ffast math
编译选项相结合,这将允许关联性,从而产生与numpy相当的性能:
%%cython -c=-ffast-math
cimport numpy as np
def sum_float_cont(np.float64_t[::1] a):
cdef:
Py_ssize_t i, n = len(a)
np.float64_t total = 0
for i in range(n):
total += a[i]
return total
现在:
>>> %timeit sum_float(a_float)
3.46 ms ± 226 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
>>> %timeit sum_float_cont(a_float)
1.87 ms ± 44 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit a_float.sum()
1.41 ms ± 88.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
成对求和双的列表
:
/*
*两两求和,舍入误差O(lgn)而不是O(n)。
*递归深度也是O(lgn)。
*更新时也更新类似的复杂浮点求和
*/
静态npy_双
成对求和双倍(npy双倍*a,npy双倍,npy双倍)
{
if(n<8){
npy_intp i;
npy_双精度=0。;
对于(i=0;i 否则,如果(n)你知道如何从所有样板文件中提取Cython生成的C代码的相关部分吗?编译器可能会优化count_int(它最终是一个乘法),但不会优化double,因为+对于浮点算术来说不是关联的
%%cython -c=-ffast-math
cimport numpy as np
def sum_float_cont(np.float64_t[::1] a):
cdef:
Py_ssize_t i, n = len(a)
np.float64_t total = 0
for i in range(n):
total += a[i]
return total
>>> %timeit sum_float(a_float)
3.46 ms ± 226 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
>>> %timeit sum_float_cont(a_float)
1.87 ms ± 44 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit a_float.sum()
1.41 ms ± 88.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)