2016-06-20 2 views
2

Изменение некоторых переменных Cython от типа int до типа size_t может значительно сократить время выполнения некоторых функций (~ 30%), но я не понимаю, почему.Cython: почему size_t быстрее, чем int?

Например:

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, и знаю Fortran лучше, чем C. Помогите мне. Какое важное различие между этими двумя типами переменных вызывает такую ​​разницу в производительности? Что я не понимаю о Китоне?

+0

вы проверили сгенерированную сборку? –

+0

@ KarolyHorvath Я попытался посмотреть сгенерированный код C, но почти ослеп. Я уверен, что он очень оптимизирован, но поскольку я больше человек из Фортрана, авто-сгенерированный C был довольно трудно читать. – theJollySin

+0

Вы берете массив 'int64_t'. Почему бы не использовать 'int64_t' для вашего аккумулятора? – user2357112

ответ

7

Вам, вероятно, придется выполнять профилирование по линиям, чтобы точно узнать, но одна вещь выделяется мне из созданного файла C: int версия проверяется на наличие отрицательных чисел, size_t считается ok.

В ИНТ цикле: (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? Есть ли вероятность, что они отличаются по размеру, вызывая изменение? В моем случае код C для uint и size_t идентичен. (Поскольку size_t является беззнаковым и, в частности беззнаковое целочисленное значение в этой системе)

+0

Хорошо, я проверил некоторое время и обнаружил, что если я использую «unsigned int» и отключу тестирование обтекания с помощью декоратора, как вы предлагаете выше, я возвращаю производительность «size_t». Итак, если вы добавите заметку о том, что «size_t» не имеет знака, я приму ваш ответ. – theJollySin

+1

Всегда выключайте проверку границ и обертывание, как только вы знаете, что делаете ... Я не знал, что использование size_t подразумевает это автоматически. – dividebyzero

2

На 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 
    

Тайминги:

%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 
Смежные вопросы