我有一个非常稀疏的矩阵,例如5000x3000,双精度浮点数。该矩阵的80%为零。我需要计算每一行的总和。所有这些都在python / cython中。我想加快这个过程。因为我需要计算该总和数百万次,所以我认为,如果我对非零元素进行索引并仅对其求和,它将更快。结果比所有零的原始“蛮力”求和要慢得多。
这是一个最小的例子:
#cython: language_level=2
import numpy as np
cimport numpy as np
import time
cdef int Ncells = 5000, KCells = 400, Ne= 350
cdef double x0=0.1, x1=20., x2=1.4, x3=2.8, p=0.2
# Setting up weight
all_weights = np.zeros( (Ncells,KCells) )
all_weights[ :Ne, :Ne ] = x0
all_weights[ :Ne, Ne: ] = x1
all_weights[Ne: , :Ne ] = x2
all_weights[Ne: , Ne: ] = x3
all_weights = all_weights * (np.random.rand(Ncells,KCells) < p)
# Making a memory view
cdef np.float64_t[:,:] my_weights = all_weights
# make an index of non zero weights
x,y = np.where( np.array(my_weights) > 0.)
#np_pawid = np.column_stack( (x ,y ) )
np_pawid = np.column_stack( (x ,y ) ).astype(int)
cdef np.int_t[:,:] pawid = np_pawid
# Making vector for column sum
summEE = np.zeros(KCells)
# Memory view
cdef np.float64_t [:] my_summEE = summEE
cdef int cc,dd,i
# brute-force summing
ntm = time.time()
for cc in range(KCells):
my_summEE[cc] = 0
for dd in range(Ncells):
my_summEE[cc] += my_weights[dd,cc]
stm = time.time()
print "BRUTE-FORCE summation : %f s"%(stm-ntm)
my_summEE[:] = 0
# summing only non zero indices
ntm = time.time()
for dd,cc in pawid:
my_summEE[cc] += my_weights[dd,cc]
stm = time.time()
print "INDEX summation : %f s"%(stm-ntm)
my_summEE[:] = 0
# summing only non zero indices unpacked by zip
ntm = time.time()
for dd,cc in zip(pawid[:,0],pawid[:,1]):
my_summEE[cc] += my_weights[dd,cc]
stm = time.time()
print "ZIPPED INDEX summation : %f s"%(stm-ntm)
my_summEE[:] = 0
# summing only non zero indices unpacked by zip
ntm = time.time()
for i in range(pawid.shape[0]):
dd = pawid[i,0]
cc = pawid[i,1]
my_summEE[cc] += my_weights[dd,cc]
stm = time.time()
print "INDEXING over INDEX summation: %f s"%(stm-ntm)
# Numpy brute-froce summing
ntm = time.time()
sumwee = np.sum(all_weights,axis=0)
stm = time.time()
print "NUMPY BRUTE-FORCE summation : %f s"%(stm-ntm)
#>
print
print "Number of brute-froce summs :",my_weights.shape[0]*my_weights.shape[1]
print "Number of indexing summs :",pawid.shape[0]
#<
我在Raspberry Pi 3上运行了它,但在PC上似乎也得到了相同的结果。
BRUTE-FORCE summation : 0.381014 s
INDEX summation : 18.479018 s
ZIPPED INDEX summation : 3.615952 s
INDEXING over INDEX summation: 0.450131 s
NUMPY BRUTE-FORCE summation : 0.013017 s
Number of brute-froce summs : 2000000
Number of indexing summs : 400820
NUMPY BRUTE-FORCE in Python : 0.029143 s
有人能解释为什么cython代码比numpy慢3-4倍吗?为什么建立索引,将求和次数从2000000减少到400820,却慢了45倍?没有任何意义。
答案 0 :(得分:1)
您在函数外部,因此可以访问全局变量。这意味着Cython每次访问它们时都必须检查它们的存在,这与它知道无法从其他地方访问的函数locals不同。
默认情况下,Cython处理负索引并进行边界检查。您可以turn these off in a number of ways。一种明显的方法是将@cython.wraparound(False)
和@cython.boundscheck(False)
作为修饰符添加到函数定义中。要知道它们实际上是做什么的-只能关闭cdef
ed numpy数组或键入的memoryviews上的这些功能,并且不能应用于其他许多东西(因此,不要将它们作为杂货店的东西而应用于所有地方) )。
查看问题可能出在哪里的一种好方法是运行cython -a <filename>
并查看带注释的html文件。黄色区域可能没有经过优化,因此您可以扩展行以查看基础的C代码。显然,仅在这方面担心经常调用的函数和循环-可以预期的是,用于设置Numpy数组的代码包含Python调用。
一些测量:
写的时候
BRUTE-FORCE summation : 0.008625 s
INDEX summation : 0.713661 s
ZIPPED INDEX summation : 0.127343 s
INDEXING over INDEX summation: 0.002154 s
NUMPY BRUTE-FORCE summation : 0.001461 s
在功能中
BRUTE-FORCE summation : 0.007706 s
INDEX summation : 0.681892 s
ZIPPED INDEX summation : 0.123176 s
INDEXING over INDEX summation: 0.002069 s
NUMPY BRUTE-FORCE summation : 0.001429 s
在具有boundscheck和wraparound的函数中:
BRUTE-FORCE summation : 0.005208 s
INDEX summation : 0.672948 s
ZIPPED INDEX summation : 0.124641 s
INDEXING over INDEX summation: 0.002006 s
NUMPY BRUTE-FORCE summation : 0.001467 s
我的建议确实有帮助,但并不太明显。我的差异并不像您看到的那么剧烈(即使您的代码未更改)。脾气暴躁仍然胜出-猜猜: