Python 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.

我正在尝试为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.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
      是否相同,并根据
      dtype
      选择要调用的BLAS函数
    • 检查数组维度并确保它们匹配
  • 。。。为输出参数分配空间

    • 分配一个新的
      np.ndarray
      对象
    • 增加该对象的引用计数
    • ndarray
  • 。。。调用提供浮点操作的BLAS操作

  • 。。。并递减传递的输入参数的refcount(检查是否应释放任何参数,尽管不会释放任何参数)

  • 然后,调用函数必须:

    • 检查
      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之间的最大区别