我是cython的新手,并为我试图优化的numpy for循环提供了以下代码。到目前为止,此Cython代码并不比numpy for循环快得多。
# cython: infer_types = True
import numpy as np
cimport numpy
DTYPE = np.double
def hdcfTransfomation(scanData):
cdef Py_ssize_t Position
scanLength = scanData.shape[0]
hdcfFunction_np = np.zeros(scanLength, dtype = DTYPE)
cdef double [::1] hdcfFunction = hdcfFunction_np
for position in range(scanLength - 1):
topShift = scanData[1 + position:]
bottomShift = scanData[:-(position + 1)]
arrayDiff = np.subtract(topShift, bottomShift)
arraySquared = np.square(arrayDiff)
arrayMean = np.mean(arraySquared, axis = 0)
hdcfFunction[position] = arrayMean
return hdcfFunction
我知道使用C数学库函数比调用numpy语言(减,平方,均值)更理想,但是我不确定在哪里可以找到可以这种方式调用的函数列表。
我一直在尝试找出通过使用不同类型等来优化此代码的方法。但没有什么能提供我认为完全优化的Cython实现所能提供的性能。
以下是numpy for循环的工作示例:
def hdcfTransfomation(scanData):
scanLength = scanData.shape[0]
hdcfFunction = np.zeros(scanLength)
for position in range(scanLength - 1):
topShift = scanData[1 + position:]
bottomShift = scanData[:-(position + 1)]
arrayDiff = np.subtract(topShift, bottomShift)
arraySquared = np.square(arrayDiff)
arrayMean = np.mean(arraySquared, axis = 0)
hdcfFunction[position] = arrayMean
return hdcfFunction
scanDataArray = np.random.rand(80000, 1)
transformedScan = hdcfTransformed(scanDataArray)
答案 0 :(得分:0)
将cython
放在一边,这是否与您当前的代码相同,但没有for
循环?我们可以收紧它并纠正错误,但首先要进行的工作是尝试将numpy中的操作应用于2D数组,然后再对cython
循环使用for
。评论太久了。
import numpy as np
# Setup
arr = np.random.choice(np.arange(10), 100).reshape(10, 10)
top_shift = arr[:, :-1]
bottom_shift = arr[:, 1:]
arr_diff = top_shift - bottom_shift
arr_squared = np.square(arr_diff)
arr_mean = arr_squared.mean(axis=1)
答案 1 :(得分:0)
始终提供尽可能多的信息(一些示例数据,Python / Cython版本,编译器版本/设置和CPU模型。
没有时间比较任何时间都非常困难。例如,此问题从SIMD矢量化中受益匪浅。使用哪种编译器,或者如果您想重新分发也应该在低端或相当老的CPUS(例如,没有AVX)上运行的编译版本,将会大有不同。
我对Cython不太熟悉,但是我认为您的主要问题是scanData
缺少声明。也许C编译器需要诸如march=native
之类的其他标志,但是真正的语法是编译器dependend。我也不确定Cython或C编译器如何优化此部分:
arrayDiff = np.subtract(topShift, bottomShift)
arraySquared = np.square(arrayDiff)
arrayMean = np.mean(arraySquared, axis = 0)
如果未加入该循环(所有矢量化命令实际上都是循环),但实际上存在像创建纯Python一样的临时arryas,这将减慢代码速度。首先创建一个1D数组是一个好主意。 (例如scanData=scanData[::1]
正如我所说的,我对Cython并不熟悉,所以我尝试了Numba的可能。至少它显示了合理的Cycyon实施也应该有的可能。
对于编译器来说可能更容易优化
import numba as nb
import numpy as np
@nb.njit(fastmath=True,error_model='numpy',parallel=True)
#scanData is a 1D-array here
def hdcfTransfomation(scanData):
scanLength = scanData.shape[0]
hdcfFunction = np.zeros(scanLength, dtype = scanData.dtype)
for position in nb.prange(scanLength - 1):
topShift = scanData[1 + position:]
bottomShift = scanData[:scanData.shape[0]-(position + 1)]
sum=0.
jj=0
for i in range(scanLength-(position + 1)):
jj+=1
sum+=(topShift[i]-bottomShift[i])**2
hdcfFunction[position] = sum/jj
return hdcfFunction
我在这里也使用了并行化,因为这个问题令人尴尬地是并行的。至少使用80_000和Numba的大小,无论您使用的是经过稍微修改的代码版本(一维数组)还是上面的代码都没关系。
时间
#Quadcore Core i7-4th gen,Numba 0.4dev,Python 3.6
scanData=np.random.rand(80_000)
#The first call to the function isn't measured (compilation overhead),but the following calls.
Pure Python: 5900ms
Numba single-threaded: 947ms
Numba parallel: 260ms
特别是对于比np.random.rand(80_000)
大的数组,可能会有更好的方法(循环耕作以更好地使用缓存),但是对于这个大小应该差不多(至少适合L3缓存)< / p>
天真的GPU实现
from numba import cuda, float32
@cuda.jit('void(float32[:], float32[:])')
def hdcfTransfomation_gpu(scanData,out_data):
scanLength = scanData.shape[0]
position = cuda.grid(1)
if position < scanLength - 1:
sum= float32(0.)
offset=1 + position
for i in range(scanLength-offset):
sum+=(scanData[i+offset]-scanData[i])**2
out_data[position] = sum/(scanLength-offset)
hdcfTransfomation_gpu[scanData.shape[0]//64,64](scanData,res_3)
这在GT640(float32)和970ms(float64)上给出了大约400ms。为了获得良好的实现,应该考虑shared arrays。