Python Cython性能基准
我正在尝试为cython和numpy制作一个翻牌基准。为此,我用cython编写了一个程序。这是:Python Cython性能基准,python,performance,benchmarking,cython,Python,Performance,Benchmarking,Cython,我正在尝试为cython和numpy制作一个翻牌基准。为此,我用cython编写了一个程序。这是: cimport numpy as np import numpy as np import time cimport cython @cython.boundscheck(False) @cython.wraparound(False) def numpybenchmark(): cdef np.ndarray[np.float64_t, ndim=2] m1 = np.random.
cimport numpy as np
import numpy as np
import time
cimport cython
@cython.boundscheck(False)
@cython.wraparound(False)
def numpybenchmark():
cdef np.ndarray[np.float64_t, ndim=2] m1 = np.random.rand(3,3)
cdef np.ndarray[np.float64_t, ndim=1] m2 = np.random.rand(3)
cdef np.ndarray[np.float64_t, ndim=1] res
cdef int niters = 10000000
cdef int x
t1 = time.time()
for x in range(niters):
res = np.dot(m1, m2)
t2 = time.time()
cdef double numopsperloop = 9. + 6.
cdef double totalops = numopsperloop * float(niters)
cdef double mflops = totalops / (t2-t1) / 1024. / 1024.
print 'Computed MFLops is: ' + str(mflops)
在我的机器上,我测量“计算的MFLops为:7.42390102416”。我的机器有一个Intel Core i7-6700HQ CPU@2.6 GHz,运行Windows 10
如果您想在您的机器上运行它,请将代码保存在名为“benchmark.pyx”的文件中。然后创建一个名为“setup.py”的文件,其中包含以下内容:
from distutils.core import setup
from Cython.Build import cythonize
import numpy
setup(
ext_modules = cythonize("benchmark.pyx"),
include_dirs=[numpy.get_include()]
)
然后您应该能够使用“python setup.py build_ext--inplace”编译它。在windows上,这可能有点困难,因为我遇到了可怕的“找不到vcvarsall.bat”错误,不得不花费大量精力来解决这个问题
我觉得这场演出很差劲。我想知道是否有人能在他们的平台上运行它,并告诉我你得到了什么?或者指出我的代码中对性能有不利影响的任何明显错误
谢谢 Cython实际上并没有消除
np.dot
上的任何Python调用开销。这包括(请注意,该列表并非详尽无遗,有些地方可能稍有错误,但它给出了要点):
np.dot
:
- 在全局命名空间中查找
np
- 在
的命名空间中查找np
的字典。(请注意,通过在函数中执行dot
,然后调用dot=np.dot
,可以消除上述所有问题)dot
- 在
的\uuuuuuu调用
点上查找词典。(如果dot是C/Fortran编译函数,则可以通过更快的机制完成)
np.dot
的参数:
- 创建一个元组,其中包含传递给
np.dot
- 增加每个参数的引用计数
np.dot
然后处理参数
- 打开元组
- 检查numpy数组中的每个参数
- 检查每个numpy数组的
是否相同,并根据dtype
选择要调用的BLAS函数dtype
- 检查数组维度并确保它们匹配
- 分配一个新的
对象np.ndarray
- 增加该对象的引用计数
- 在
ndarray
- 检查
np.dot
- 接收输出数组(这里可能有一些refcount杂耍)
- 递减
res先前内容的refcount
- 释放
之前的内容,记住这至少是一个两步过程,因为数组与res
保持器分开保存ndarray
如果您想使大部分(可能除了分配)与矩阵向量乘法相比变得无关紧要,那么您需要在更大的阵列上进行测量。您可以使用
np.dot
中的out
可选参数取消分配。如果你想让这一切都消失,那么你可以使用直接调用BLAS函数。在仔细阅读了DavidW的文章并做了一些实验后,我找到了一种避免所有numpy开销的方法。它涉及到使用指针,特别是不将numpy数组传递给循环中的函数
以下是完整的代码:
cimport numpy as np
import numpy as np
import time
cdef matrixdotvector(double* mat, int numrows, int numcols, double* vec, double* outputvec):
outputvec[0] = mat[0+0*numcols] * vec[0] + mat[1+0*numcols] * vec[1] + mat[2+0*numcols] * vec[2]
outputvec[1] = mat[0+1*numcols] * vec[0] + mat[1+1*numcols] * vec[1] + mat[2+1*numcols] * vec[2]
outputvec[2] = mat[0+2*numcols] * vec[0] + mat[1+2*numcols] * vec[1] + mat[2+2*numcols] * vec[2]
cimport cython
@cython.boundscheck(False)
@cython.wraparound(False)
def numpybenchmark():
cdef np.ndarray[np.float64_t, ndim=2] m1 = np.random.rand(3,3)
cdef np.ndarray[np.float64_t, ndim=1] m2 = np.transpose(np.random.rand(3))
cdef np.ndarray[np.float64_t, ndim=1] res
cdef int niters = 10000000
cdef int x
t1 = time.time()
for x in range(niters):
res = np.dot(m1, m2)
t2 = time.time()
cdef double numopsperloop = 9. + 6.
cdef double totalops = numopsperloop * float(niters)
cdef double mflops = totalops / (t2-t1) / 1024. / 1024.
print 'Computed MFLops is: ' + str(mflops)
cimport cython
@cython.boundscheck(False)
@cython.wraparound(False)
def numpybenchmark2():
cdef int numrows = 3
cdef int numcols = 3
cdef np.ndarray[np.float64_t, ndim=2] m1 = np.random.rand(3,3)
cdef np.ndarray[np.float64_t, ndim=1] m2 = np.transpose(np.random.rand(3))
cdef np.ndarray[np.float64_t, ndim=1] res = np.zeros(3)
cdef int niters = 10000000
cdef int x
t1 = time.time()
for x in range(niters):
matrixdotvector(&m1[0,0], numrows, numcols, &m2[0], &res[0])
t2 = time.time()
assert (np.linalg.norm(np.dot(m1,m2) - res) < 1.0e-6), "Arrays do not match"
cdef double numopsperloop = 9. + 6.
cdef double totalops = numopsperloop * float(niters)
cdef double mflops = totalops / (t2-t1) / 1024. / 1024.
print 'Computed MFLops is: ' + str(mflops)
cimport numpy作为np
将numpy作为np导入
导入时间
cdef matrixdotvector(双*矩阵、整数行、整数行、双*向量、双*输出向量):
输出向量[0]=mat[0+0*numcols]*vec[0]+mat[1+0*numcols]*vec[1]+mat[2+0*numcols]*vec[2]
输出向量[1]=mat[0+1*numcols]*vec[0]+mat[1+1*numcols]*vec[1]+mat[2+1*numcols]*vec[2]
输出向量[2]=mat[0+2*numcols]*vec[0]+mat[1+2*numcols]*vec[1]+mat[2+2*numcols]*vec[2]
西姆波特赛顿酒店
@cython.boundscheck(错误)
@cython.wrapparound(假)
def numpybenchmark():
cdef np.ndarray[np.float64_t,ndim=2]m1=np.rand.rand(3,3)
cdef np.ndarray[np.float64_t,ndim=1]m2=np.transpose(np.random.rand(3))
cdef np.ndarray[np.float64_t,ndim=1]res
cdef整数=10000000
cdef int x
t1=时间。时间()
对于范围内的x(硝酸盐):
res=np.点(m1,m2)
t2=时间。时间()
cdef双numopsperloop=9+6.
cdef double totalops=numopsperloop*浮点(硝酸盐)
cdef双mflops=totalops/(t2-t1)/1024./1024
打印“计算的MFLops为:”+str(MFLops)
西姆波特赛顿酒店
@cython.boundscheck(错误)
@cython.wrapparound(假)
def numpybenchmark2():
cdef int numrows=3
cdef int numcols=3
cdef np.ndarray[np.float64_t,ndim=2]m1=np.rand.rand(3,3)
cdef np.ndarray[np.float64_t,ndim=1]m2=np.transpose(np.random.rand(3))
cdef np.ndarray[np.float64_t,ndim=1]res=np.zero(3)
cdef整数=10000000
cdef int x
t1=时间。时间()
对于范围内的x(硝酸盐):
matrixdotvector(&m1[0,0],numrows,numcols,&m2[0],&res[0])
t2=时间。时间()
断言(np.linalg.norm(np.dot(m1,m2)-res)<1.0e-6),“数组不匹配”
cdef双numopsperloop=9+6.
cdef double totalops=numopsperloop*浮点(硝酸盐)
cdef双mflops=totalops/(t2-t1)/1024./1024
打印“计算的MFLops为:”+str(MFLops)
numpybenchmark()和numpybenchmar之间的最大区别