【发布时间】:2022-01-19 16:07:12
【问题描述】:
我正在尝试通过在点积操作np.dot(a,b) 上超越 Numpy 来学习 cython。但我的实现慢了大约 4 倍。
所以,这是我的 hello.pyx 文件 cython 实现:
cimport numpy as cnp
cnp.import_array()
cpdef double dot_product(double[::1] vect1, double[::1] vect2):
cdef int size = vect1.shape[0]
cdef double result = 0
cdef int i = 0
while i < size:
result += vect1[i] * vect2[i]
i += 1
return result
这是我的 .py 测试文件:
import timeit
setup = '''
import numpy as np
import hello
n = 10000
a = np.array([float(i) for i in range(n)])
b = np.array([i/2 for i in a])
'''
lf_code = 'res_lf = hello.dot_product(a, b)'
np_code = 'res_np = np.dot(a,b)'
n = 100
lf_time = timeit.timeit(lf_code, setup=setup, number=n) * 100
np_time = timeit.timeit(np_code, setup=setup, number=n) * 100
print(f'Lightning fast time: {lf_time}.')
print(f'Numpy time: {np_time}.')
控制台输出:
Lightning fast time: 0.12186000000156127.
Numpy time: 0.028800000001183435.
构建 hello.pyx 的命令:
python setup.py build_ext --inplace
setup.py 文件:
from distutils.core import Extension, setup
from Cython.Build import cythonize
import numpy as np
# define an extension that will be cythonized and compiled
ext = Extension(name="hello", sources=["hello.pyx"], include_dirs=[np.get_include()])
setup(ext_modules=cythonize(ext))
处理器: i7-7700T @ 2.90 GHz
【问题讨论】:
-
你正在与OpenBLAS(或类似的)竞争:numpy 只包装这个计算。也许你的野心太大了。 OpenBLAS 和公司。具有 CPU 架构特定的内核、SIMD、多线程和汇编代码。
-
我认为你不能比 numpy 做得更好,但挑战很好。 Numpy 使用向量指令,这些指令应该同时处理多个数字。顺便说一句,使用循环并不是进行矩阵乘法的最佳方式。我建议你看看 Strassen 和 Coppersmith 算法。如果你想比numpy做得更好,就用显卡; Numba cuda 可以毫不费力地进行并行计算。
-
你不是第一个尝试失败的人:stackoverflow.com/q/10442365/5769463 cython 比 c 还要难,因为 cython 可以生成不易优化的 c 代码
-
就其价值而言,仅比 OpenBLAS 慢 4 倍意味着您可能在不采取极端措施的情况下尽可能地做到了。
-
@Neofelis(和@ead)这不是矩阵乘法而是点积。因此,Strassen 和 Coppersmith 算法在这里不相关。此外,不,GPU 对于这样的任务不会更快,因为数组太小了。在 GPU 上执行内核的延迟通常高于这个时间(GPU 针对大工作负载进行了优化,显然不是低延迟)。更不用说向 GPU 传输数据的速度很慢,而且这种操作通常受内存限制(就像所有 BLAS 1 级原语一样)。
标签: python numpy performance cython