为什么在我使用 Cython 的情况下,为 C 连续数组赋值很慢
Why assign values to C-contiguous array is slow in my case using Cython
我遇到了使用 Cython 将临时结果分配给数组的问题。这里我声明了一个 test_array
、sample-size
和 weight_array
,并通过使用 for
循环,我将每个加权结果保存到一个 res_array
中。 test_array
和 weight_array
在 Cython 中都被定义为 C 连续数组。 test.pyx 和 setup.py 文件列表如下:
# test.pyx
import numpy as np
cimport numpy as np
import random
cimport cython
from cython cimport boundscheck, wraparound
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.cdivision(True)
@cython.profile(True)
def cython_sample(int res_size, int sample_size, double[::1] all_data, double[::1] weight_array):
# using c-contiguous array can speed up a little bit
cdef int ii, jj
cdef double tmp_res, dot_result
cdef double[::1] tmp_sample = np.ones(sample_size, dtype=np.double)
cdef double[::1] res_array = np.ones(res_size, dtype=np.double)
ran = random.normalvariate # generate random value as a test
for ii in range(res_size):
tmp_sample = all_data[ii:(ii + sample_size)]
# inner product operation
dot_result = 0.0
for jj in range(sample_size):
dot_result += tmp_sample[jj]*weight_array[jj]
# save inner product result into array
res_array[ii] = dot_result
#res_array[ii] = ran(10000,20000)
return res_array
# setup.py
from setuptools import setup,find_packages
from distutils.extension import Extension
from Cython.Build import cythonize
import numpy as np
ext = Extension("mycython.test", sources=["mycython/test.pyx"])
setup(ext_modules=cythonize(ext),
include_dirs=[np.get_include()],
name="mycython",
version="0.1",
packages=find_packages(),
author="me",
author_email="me@example.com",
url="http://example.com/")
而 python test.py 是:
import time
import random
import numpy as np
from strategy1 import __cyn__
sample_size = 3000
test_array = [random.random() for _ in range(300000)]
res_size = len(test_array) - sample_size + 1
weight_array = [random.random() for _ in range(sample_size)]
c_contig_store_array = np.ascontiguousarray(test_array, dtype=np.double)
c_contig_weigh_array = np.ascontiguousarray(weight_array, dtype=np.double)
replay = 100
start_time = time.time()
for ii in range(int(replay)):
__cyn__.cython_sample(res_size, sample_size, c_contig_store_array, c_contig_weigh_array)
per_elapsed_time = (time.time() - start_time) / replay
print('Elapse time :: %g sec' % (per_elapsed_time))
所以我测试了两种情况:
# 1. when saving dot_result into 'res_array':
res_array[ii] = dot_result
速度测试显示:Elapse time :: 0.821084 sec
# 2. when saving a random value ran(10000,20000) into 'res_array':
res_array[ii] = ran(10000,20000)
速度测试显示:Elapse time :: 0.214591 sec
。
之所以用ran(*,*)
来测试代码,是因为我发现如果把原代码中的res_array[ii] = dot_result
和res_array[ii] = ran(10000,20000)
都注释掉,速度几乎可以提高30 -100 次 (Elapse time :: 0.00633394 sec
)。然后我认为问题可能在于将 dot_result
值分配给 res_array
,这变成了真的,因为将随机生成的双精度值 ran(10000,20000)
分配给 res_array
的速度非常快快(如上所示快了近 4 倍)。
有什么办法可以解决这个问题吗?谢谢
如果不使用dot_result
的值,编译器会去掉循环:
dot_result = 0.0
for jj in range(sample_size):
dot_result += tmp_sample[jj]*weight_array[jj]
内部循环占用了大部分计算时间。
你的 cython 代码看起来像 correlate()
,你可以使用 fft 来加速它:
from scipy import signal
res = signal.fftconvolve(c_contig_store_array, c_contig_weigh_array[::-1], mode="valid")
我遇到了使用 Cython 将临时结果分配给数组的问题。这里我声明了一个 test_array
、sample-size
和 weight_array
,并通过使用 for
循环,我将每个加权结果保存到一个 res_array
中。 test_array
和 weight_array
在 Cython 中都被定义为 C 连续数组。 test.pyx 和 setup.py 文件列表如下:
# test.pyx
import numpy as np
cimport numpy as np
import random
cimport cython
from cython cimport boundscheck, wraparound
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.cdivision(True)
@cython.profile(True)
def cython_sample(int res_size, int sample_size, double[::1] all_data, double[::1] weight_array):
# using c-contiguous array can speed up a little bit
cdef int ii, jj
cdef double tmp_res, dot_result
cdef double[::1] tmp_sample = np.ones(sample_size, dtype=np.double)
cdef double[::1] res_array = np.ones(res_size, dtype=np.double)
ran = random.normalvariate # generate random value as a test
for ii in range(res_size):
tmp_sample = all_data[ii:(ii + sample_size)]
# inner product operation
dot_result = 0.0
for jj in range(sample_size):
dot_result += tmp_sample[jj]*weight_array[jj]
# save inner product result into array
res_array[ii] = dot_result
#res_array[ii] = ran(10000,20000)
return res_array
# setup.py
from setuptools import setup,find_packages
from distutils.extension import Extension
from Cython.Build import cythonize
import numpy as np
ext = Extension("mycython.test", sources=["mycython/test.pyx"])
setup(ext_modules=cythonize(ext),
include_dirs=[np.get_include()],
name="mycython",
version="0.1",
packages=find_packages(),
author="me",
author_email="me@example.com",
url="http://example.com/")
而 python test.py 是:
import time
import random
import numpy as np
from strategy1 import __cyn__
sample_size = 3000
test_array = [random.random() for _ in range(300000)]
res_size = len(test_array) - sample_size + 1
weight_array = [random.random() for _ in range(sample_size)]
c_contig_store_array = np.ascontiguousarray(test_array, dtype=np.double)
c_contig_weigh_array = np.ascontiguousarray(weight_array, dtype=np.double)
replay = 100
start_time = time.time()
for ii in range(int(replay)):
__cyn__.cython_sample(res_size, sample_size, c_contig_store_array, c_contig_weigh_array)
per_elapsed_time = (time.time() - start_time) / replay
print('Elapse time :: %g sec' % (per_elapsed_time))
所以我测试了两种情况:
# 1. when saving dot_result into 'res_array':
res_array[ii] = dot_result
速度测试显示:Elapse time :: 0.821084 sec
# 2. when saving a random value ran(10000,20000) into 'res_array':
res_array[ii] = ran(10000,20000)
速度测试显示:Elapse time :: 0.214591 sec
。
之所以用ran(*,*)
来测试代码,是因为我发现如果把原代码中的res_array[ii] = dot_result
和res_array[ii] = ran(10000,20000)
都注释掉,速度几乎可以提高30 -100 次 (Elapse time :: 0.00633394 sec
)。然后我认为问题可能在于将 dot_result
值分配给 res_array
,这变成了真的,因为将随机生成的双精度值 ran(10000,20000)
分配给 res_array
的速度非常快快(如上所示快了近 4 倍)。
有什么办法可以解决这个问题吗?谢谢
如果不使用dot_result
的值,编译器会去掉循环:
dot_result = 0.0
for jj in range(sample_size):
dot_result += tmp_sample[jj]*weight_array[jj]
内部循环占用了大部分计算时间。
你的 cython 代码看起来像 correlate()
,你可以使用 fft 来加速它:
from scipy import signal
res = signal.fftconvolve(c_contig_store_array, c_contig_weigh_array[::-1], mode="valid")