【发布时间】:2018-08-29 13:17:35
【问题描述】:
我正在使用 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
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和wraparound指令相同。为什么? - 将
sum_int中的整数numpy 数组转换为C 指针(np.int64_t *arr = <np.int64_t *> a.data) 可将性能再提高25%。对花车这样做没有任何作用
主要问题
如何在 Cython 中使用浮点数获得与使用整数相同的性能?
编辑 - 只是计数很慢?!?
我写了一个更简单的函数,它只计算迭代次数。第一个将计数存储为 int,后者存储为 double。
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_double 与上面的 sum_float 具有 same 性能。这太疯狂了……
%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)
【问题讨论】:
-
你知道如何从所有样板文件中提取 Cython 生成的 C 代码的相关部分吗?
-
编译器可能会针对 count_int 进行优化(最后是乘法),但不会针对 double 进行优化,因为 + 与浮点算术无关