cython的慢分裂

2024-04-28 23:53:03 发布

您现在位置:Python中文网/ 问答频道 /正文

为了在cython中快速除法,我可以使用编译器指令

@cython.cdivision(True)

这是可行的,因为生成的c代码没有零除法检查。但是由于某些原因,它实际上使我的代码变慢了。下面是一个例子:

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.cdivision(True)
def example1(double[:] xi, double[:] a, double[:] b, int D):

    cdef int k
    cdef double[:] x = np.zeros(D)

    for k in range(D):
        x[k] = (xi[k] - a[k]) / (b[k] - a[k]) 

    return x

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
def example2(double[:] xi, double[:] a, double[:] b, int D):

    cdef int k
    cdef double[:] x = np.zeros(D)

    for k in range(D):
        x[k] = (xi[k] - a[k]) / (b[k] - a[k]) 

    return x

def test_division(self):

    D = 10000
    x = np.random.rand(D)
    a = np.zeros(D)
    b = np.random.rand(D) + 1

    tic = time.time()
    example1(x, a, b, D)
    toc = time.time()

    print 'With c division: ' + str(toc - tic)

    tic = time.time()
    example2(x, a, b, D)
    toc = time.time()

    print 'Without c division: ' + str(toc - tic)

这将导致输出:

With c division: 0.000194787979126
Without c division: 0.000176906585693

是否有任何原因可以让关闭零除法检查减慢速度(我知道没有零除法)。


Tags: falsetruetimedefnpzerosticcython
2条回答

我的问题是我在集会上看到了一切。试图用一种语言告诉另一种语言做我想做的事情,以便提取性能似乎比需要的更令人沮丧和困难。例如,西摩克雷总是用这种方式重铸师。C=A/B变成:

R = reciprocalApprox(B);
R = reciprocalImprove(R);   //M-Add performed 3x to get an accurate 1/B
C = A * R;

有一次有人问克雷为什么要使用牛顿-拉斐逊方法,他解释说,通过管道进行的操作比使用硬件划分要多。AMD的3DNow也采用了类似的方法,不过采用了32位浮点运算。对于使用gcc的SSE,尝试-mrecip标记,以及-funsafe-math-optimizations, -finite-math-only-fno-trapping-math。臭名昭著的-ffast-math选项也实现了这一点。你失去了2个超视距单位,或者在最后一个地方失去了个单位。

http://gcc.gnu.org/onlinedocs/gcc/i386-and-x86_002d64-Options.html

你甚至可能想试试libdivide.h(在libdivide.com上)。它非常依赖内存,使用一系列“廉价”的移位和乘法运算,最终比整数除法快10倍左右。它还为编译器生成C或C++代码。

首先,您需要多次调用函数(>;1000),并取每个函数所用时间的平均值,以准确了解它们之间的差异。一次调用每个函数将不够精确。

其次,在函数中花费的时间将受到其他因素的影响,而不仅仅是带有除法的循环。调用这样的def即Python函数需要一些传递和返回参数的开销。此外,在函数中创建numpy数组需要时间,因此两个函数中的循环中的任何差异都不会太明显。

最后,请参见这里(https://github.com/cython/cython/wiki/enhancements-compilerdirectives),将c-division指令设置为False将导致大约35%的速度损失。我认为这还不足以显示在你的例子中,考虑到其他间接费用。我检查了Cython输出的C代码,示例2的代码明显不同,并且包含额外的零除检查,但是当我分析它时,运行时的差异可以忽略不计。

为了说明这一点,我运行了下面的代码,在这里我将您的代码并将def函数变成cdef函数,即Cython函数,而不是Python函数。这大大减少了传递和返回参数的开销。我还修改了example1example2来计算numpy数组中的值的和,而不是创建一个新数组并填充它。这意味着几乎所有在每个函数中花费的时间现在都在循环中,所以应该更容易看到任何差异。我也运行了很多次每个函数,使D更大。

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.cdivision(True) 
@cython.profile(True)
cdef double example1(double[:] xi, double[:] a, double[:] b, int D):

    cdef int k
    cdef double theSum = 0.0

    for k in range(D):
        theSum += (xi[k] - a[k]) / (b[k] - a[k])

    return theSum

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.profile(True)
@cython.cdivision(False)
cdef double example2(double[:] xi, double[:] a, double[:] b, int D):

    cdef int k
    cdef double theSum = 0.0

    for k in range(D):
        theSum += (xi[k] - a[k]) / (b[k] - a[k])

    return theSum


def testExamples():
    D = 100000
    x = np.random.rand(D)
    a = np.zeros(D)
    b = np.random.rand(D) + 1

    for i in xrange(10000):
        example1(x, a, b, D)
        example2(x, a, b,D) 

我通过profiler(python-m cProfile-s cumulative)运行了这段代码,相关输出如下:

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
10000    1.546    0.000    1.546    0.000 test.pyx:26(example2)
10000    0.002    0.000    0.002    0.000 test.pyx:11(example1)

这表明示例2要慢得多。如果我在示例2中打开c除法,那么对于示例1和示例2,花费的时间是相同的,所以这显然有一个显著的效果。

相关问题 更多 >