对于我的项目使用,我需要在二维数组中存储一定数量(~100x100)的浮点数。在函数计算期间,我需要读取和写入数组,因为函数确实是瓶颈(消耗98%的时间)我真的需要快速。
我用numpy和cython进行了一些实验:
import numpy
import time
cimport numpy
cimport cython
cdef int col, row
DTYPE = numpy.int
ctypedef numpy.int_t DTYPE_t
cdef numpy.ndarray[DTYPE_t, ndim=2] matrix_c = numpy.zeros([100 + 1, 100 + 1], dtype=DTYPE)
time_ = time.time()
for l in xrange(5000):
for col in xrange(100):
for row in xrange(100):
matrix_c[<unsigned int>row + 1][<unsigned int>col + 1] = matrix_c[<unsigned int>row][<unsigned int>col]
print "Numpy + cython time: {0}".format(time.time() - time_)
但是我发现尽管我尝试了所有尝试,使用python列表的版本仍然明显更快。
使用列表的代码:
matrix = []
for i in xrange(100 + 1):
matrix.append([])
for j in xrange(100 + 1):
matrix[i].append(0)
time_ = time.time()
for l in xrange(5000):
for col in xrange(100):
for row in xrange(100):
matrix[row + 1][col + 1] = matrix[row][col]
print "list time: {0}".format(time.time() - time_)
结果:
list time: 0.0141758918762
Numpy + cython time: 0.484772920609
我做错了吗?如果没有,有什么能帮助我改善结果吗?
答案 0 :(得分:2)
这是我的代码版本。 有三个函数,分别处理整数数组,32位浮点数组和双精度浮点数组。
from numpy cimport ndarray as ar
cimport numpy as np
import numpy as np
cimport cython
import time
@cython.boundscheck(False)
@cython.wraparound(False)
def access_write_int(ar[int,ndim=2] c, int n):
cdef int l, col, row, h=c.shape[0], w=c.shape[1]
time_ = time.time()
for l in range(n):
for row in range(h-1):
for col in range(w-1):
c[row+1,col+1] = c[row,col]
print "Numpy + cython time: {0}".format(time.time() - time_)
@cython.boundscheck(False)
@cython.wraparound(False)
def access_write_float(ar[np.float32_t,ndim=2] c, int n):
cdef int l, col, row, h=c.shape[0], w=c.shape[1]
time_ = time.time()
for l in range(n):
for row in range(h-1):
for col in range(w-1):
c[row+1,col+1] = c[row,col]
print "Numpy + cython time: {0}".format(time.time() - time_)
@cython.boundscheck(False)
@cython.wraparound(False)
def access_write_double(ar[double,ndim=2] c, int n):
cdef int l, col, row, h=c.shape[0], w=c.shape[1]
time_ = time.time()
for l in range(n):
for row in range(h-1):
for col in range(w-1):
c[row+1,col+1] = c[row,col]
print "Numpy + cython time: {0}".format(time.time() - time_)
要从Python调用这些函数,我运行
import numpy as np
from numpy.random import rand, randint
print "integers"
c = randint(0, high=20, size=(101,101))
access_write_int(c, 5000)
print "32 bit float"
c = rand(101, 101).astype(np.float32)
access_write_float(c, 5000)
print "double precision"
c = rand(101, 101)
access_write_double(c, 5000)
以下更改非常重要:
通过使用[i,j]
而不是[i][j]
将变量l
,col
和row
定义为整数,以便for循环以C运行。
使用函数装饰器@cython.boundscheck(False)
和'@ cython.wraparound(False)`来关闭程序关键部分的boundschecking和wraparound索引。这允许超出范围的内存访问,因此当您确定您的索引是它们应该是什么时,您应该只执行 。
交换两个最里面的for
循环,以便根据数组在内存中的排列方式访问数组。这对较大的阵列产生了更大的影响。由np.zeros
np.random.rand
等给出的数组通常是C连续的,因此行存储在连续的块中,并且沿着外部for
循环中的行改变索引的速度更快不是内在的。如果你想保持for循环,可以考虑在运行函数之前对数组进行转置,这样就可以使列成为连续的块。
答案 1 :(得分:1)
问题似乎是你访问矩阵元素的方式。
使用[i,j]
代替[i][j]
。
此外,您可以删除转换<>
,这可以防止错误的值被占用,但会增加函数调用开销。
另外,我会使用range
而不是xrange
,因为在所有Cython中他们使用的文档中的示例range
。
结果如下:
import numpy
import time
cimport numpy
cimport cython
cdef int col, row
INT = numpy.int
ctypedef numpy.int_t cINT
cdef numpy.ndarray[cINT, ndim=2] matrix_c = numpy.zeros([100 + 1, 100 + 1], dtype=INT)
time_ = time.time()
for l in range(5000):
for col in range(100):
for row in range(100):
matrix_c[row + 1, col + 1] = matrix_c[row, col]
print "Numpy + cython time: {0}".format(time.time() - time_)
强烈建议参考: