Cython:为什么size_t比int快?

时间:2016-06-20 22:07:23

标签: python performance types cython

将某些Cython变量从类型int更改为类型size_t可以显着减少某些函数时间(~30%),但我不明白为什么。

例如:

cimport numpy as cnp
import numpy as np

def sum_int(cnp.int64_t[::1] A):
    cdef unsigned long s = 0
    cdef int k
    for k in xrange(A.shape[0]):
        s += A[k]
    return s

def sum_size_t(cnp.int64_t[::1] A):
    cdef unsigned long s = 0
    cdef size_t k
    for k in xrange(A.shape[0]):
        s += A[k]
    return s

a = np.array(range(1000000))

时间结果:

In [17]: %timeit sum_int(a)   
1000 loops, best of 3: 652 µs per loop

In [18]: %timeit sum_size_t(a)
1000 loops, best of 3: 427 µs per loop

我是Cython的新手,比C.更了解Fortran。帮助我。这两种变量类型之间的重要区别是什么导致了这种性能差异?什么是我不喜欢Cython?

2 个答案:

答案 0 :(得分:9)

您可能不得不进行逐行分析才能准确找到,但有一件事从我生成的C文件中脱颖而出:检查int版本是否为负数,size_t假设{1}}正常。

在int循环中:(t_3是从k分配的,它们是相同的类型)

if (__pyx_t_3 < 0) {
  __pyx_t_3 += __pyx_v_A.shape[0];
  if (unlikely(__pyx_t_3 < 0)) __pyx_t_4 = 0;
} else if (unlikely(__pyx_t_3 >= __pyx_v_A.shape[0])) __pyx_t_4 = 0;

在size_t循环中:

if (unlikely(__pyx_t_3 >= (size_t)__pyx_v_A.shape[0])) __pyx_t_4 = 0;

因此,不需要进行环绕测试,因为size_t是无符号的,并且保证在索引内存中的项时不会回滚。其余几乎是一样的。

更新:关于您的unsigned int结果 - 您的int和size_t的大小是多少?他们有什么不同的大小,导致变化?在我的例子中,uint和size_t的C代码是相同的。 (因为size_t是无符号的,在此系统上特别是unsigned int)

答案 1 :(得分:3)

在64位系统上,似乎有两个原因:

  1. 对循环使用无符号整数:

    %%cython
    
    cimport numpy as cnp
    import numpy as np
    
    def sum_int_unsigned(cnp.int64_t[::1] A):
        cdef unsigned long s = 0
        cdef unsigned k
        for k in xrange(A.shape[0]):
            s += A[k]
        return s
    
  2. 使用long代替int

    %%cython
    
    cimport numpy as cnp
    import numpy as np
    
    def sum_int_unsigned_long(cnp.int64_t[::1] A):
        cdef unsigned long s = 0
        cdef unsigned long k
        for k in xrange(A.shape[0]):
            s += A[k]
        return s
    
  3. 时序:

    %timeit sum_int(a)
    1000 loops, best of 3: 1.52 ms per loop
    
    %timeit sum_size_t(a)
    1000 loops, best of 3: 671 µs per loop
    

    使用unsigned为我们带来了一半:

    %timeit sum_int_unsigned(a) 
    1000 loops, best of 3: 1.09 ms per loop
    

    其余使用long帐户:

    %timeit sum_int_unsigned_long(a)
    1000 loops, best of 3: 648 µs per loop