提升Python + numpy数组分配/初始化性能
我正在写一个Python程序,使用一些来自DLL的外部功能。我的问题是如何在C代码和Python中的矩阵(numpy数组)之间传递数据。目前我使用以下代码从DLL接收数据:
peak_count = ct.c_int16()
peak_wl_array = np.zeros(512, dtype=np.double)
peak_pwr_array = np.zeros(512, dtype=np.double)
res = __dll.DLL_Search_Peaks(ctypes.c_int(data.shape[0])
ctypes.c_void_p(data_array.ctypes.data),
ctypes.c_void_p(peak_wl_array.ctypes.data),
ctypes.c_void_p(peak_pwr_array.ctypes.data),
ct.byref(peak_count))
这个方法运行得很好,但我的问题是numpy的分配速度——即使不调用DLL(只是注释掉),我每100,000次调用也要花3.1秒。
我只是用np.zeros()来分配内存,然后用ctypes.c_void_p(D.ctypes.data)获取可写指针。
我需要每秒处理大约20,000次调用,所以几乎所有的时间都花在了分配内存上。
我考虑过使用cython,但它并不能加速numpy数组,所以我不会有什么收益。
有没有更快的方法从C编写的DLL接收类似矩阵的数据呢?
1 个回答
2
内存操作是比较耗费资源的,不管是用numpy还是其他方式。
如果你需要分配很多数组,最好是只分配一次,然后用视图或者子数组来只使用数组的一部分,这样会更高效:
import numpy as np
niters=10000
asize=512
def forig():
for i in xrange(niters):
peak_wl_array = np.empty((asize), dtype=np.double)
peak_pwr_array = np.empty((asize), dtype=np.double)
return peak_pwr_array
def fviews():
peak_wl_arrays = np.empty((asize*niters), dtype=np.double)
peak_pwr_arrays = np.empty((asize*niters), dtype=np.double)
for i in xrange(niters):
# create views
peak_wl_array = peak_wl_arrays[i*asize:(i+1)*asize]
peak_pwr_array = peak_pwr_arrays[i*asize:(i+1)*asize]
# then do something
return peak_pwr_emptys
def fsubemptys():
peak_wl_arrays = np.empty((niters,asize), dtype=np.double)
peak_pwr_arrays = np.empty((niters,asize), dtype=np.double)
for i in xrange(niters):
# do something with peak_wl_arrays[i,:]
return peak_pwr_emptys
import timeit
print timeit.timeit(forig,number=100)
print timeit.timeit(fviews,number=100)
print timeit.timeit(fsubemptys,number=100)
运行结果是
3.41996979713
0.844147920609
0.00169682502747
注意,如果你使用的是np.zeros这样的函数,那么你大部分时间是在初始化内存,而不是分配内存,这个过程会花费更多时间,几乎会消除这两种方法之间的差别:
4.20200014114
5.43090081215
4.58127593994
在较新的系统上,单线程访问主内存的速度大约是10GB/s(每秒1亿个双精度浮点数),所以每次调用大约需要
1024个双精度浮点数/次调用 / (1亿个双精度浮点数/秒) ~ 1微秒/次调用
来清空内存,这已经占用了你看到的执行时间的相当一部分。不过,如果你在进行调用之前先初始化一个大的数组,那么总的执行时间会保持不变,但每次调用的延迟会更低。