用于python的快速2维浮点数组(访问/写入)

时间:2013-07-07 19:42:54

标签: python arrays optimization cython

对于我的项目使用,我需要在二维数组中存储一定数量(~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

我做错了吗?如果没有,有什么能帮助我改善结果吗?

2 个答案:

答案 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)

以下更改非常重要:

  1. 通过使用[i,j]而不是[i][j]

  2. 形式的索引访问数组来避免切片。
  3. 将变量lcolrow定义为整数,以便for循环以C运行。

  4. 使用函数装饰器@cython.boundscheck(False)和'@ cython.wraparound(False)`来关闭程序关键部分的boundschecking和wraparound索引。这允许超出范围的内存访问,因此当您确定您的索引是它们应该是什么时,您应该只执行

  5. 交换两个最里面的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_)

强烈建议参考:

- Working with NumPy in Cython