2015-05-27 7 views
1

Рассмотрим следующий пример делать в InPlace добавьте на memoryview Cython:векторизации зацикливание на массив из Cython

#cython: boundscheck=False, wraparound=False, initializedcheck=False, nonecheck=False, cdivision=True 
from libc.stdlib cimport malloc, free 
from libc.stdio cimport printf 
cimport numpy as np 
import numpy as np 


cdef extern from "time.h": 
    int clock() 


cdef void inplace_add(double[::1] a, double[::1] b): 
    cdef int i 
    for i in range(a.shape[0]): 
     a[i] += b[i] 


cdef void inplace_addlocal(double[::1] a, double[::1] b): 
    cdef int i, n = a.shape[0] 
    for i in range(n): 
     a[i] += b[i] 


def main(int N): 
    cdef: 
     int rep = 1000000, i 
     double* pa = <double*>malloc(N * sizeof(double)) 
     double* pb = <double*>malloc(N * sizeof(double)) 
     double[::1] a = <double[:N]>pa 
     double[::1] b = <double[:N]>pb 
     int start 
    start = clock() 
    for i in range(N): 
     a[i] = b[i] = 1./(1 + i) 
    for i in range(rep): 
     inplace_add(a, b) 
    printf("loop %i\n", clock() - start) 
    print(np.asarray(a)[:4]) 
    start = clock() 
    for i in range(N): 
     a[i] = b[i] = 1./(1 + i) 
    for i in range(rep): 
     inplace_addlocal(a, b) 
    printf("loop_local %i\n", clock() - start) 
    print(np.asarray(a)[:4]) 

С этими директивами Cython, казалось бы, эквивалентный inplace_add и inplace_addlocal как компилировать плотных петель C , Но для N=128 (приблизительный размер, который я ожидаю) inplace_addlocal в два раза (!) Быстрее, чем inplace_add, после компиляции с gcc -Ofast (и прямое написание функции C, принимающей (int, double *, double *), является более или менее быстрым как addlocal, с или без #openmp simd). Пройдя -fopt-info до gcc, показано, что inplace_addlocal получает векторный рисунок, но не inplace_add.

Это проблема с кодом C, который генерирует Cython (т. Е. Gcc действительно не может вывести любые гарантии, необходимые для векторизации кода) или с gcc (т. Е. Некоторая оптимизация отсутствует) или что-то еще?

Спасибо.

(кросс-размещены на Cython пользователей)

ответ

1

Единственное различие для генерируемого кода C является то, что в inplace_addlocal конец переменная для цикла является int, в то время как в inplace_add это Py_ssize_t.

Поскольку ваш счетчик циклов является int, в версии inplace_add, при выполнении сравнения будут существовать дополнительные накладные расходы из-за литья между двумя типами.

inplace_add (соответствующий раздел)

Py_ssize_t __pyx_t_1; 
int __pyx_t_2; 
int __pyx_t_3; 
int __pyx_t_4; 

__pyx_t_1 = (__pyx_v_a.shape[0]); 
for (__pyx_t_2 = 0; __pyx_t_2 < __pyx_t_1; __pyx_t_2+=1) { 
    __pyx_v_i = __pyx_t_2; 

inplace_addlocal (соответствующий раздел)

int __pyx_t_1; 
int __pyx_t_2; 
int __pyx_t_3; 
int __pyx_t_4; 

__pyx_v_n = (__pyx_v_a.shape[0]); 
__pyx_t_1 = __pyx_v_n; 
for (__pyx_t_2 = 0; __pyx_t_2 < __pyx_t_1; __pyx_t_2+=1) { 
    __pyx_v_i = __pyx_t_2; 

Этот answer упоминает, что является предпочтительным использование Py_ssize_t для индексов (и надо полагать, по умолчанию в Cython), что позволило бы решить эту проблему.

+0

Хороший улов. Любое объявление 'i' как' size_t' или литье 'a.shape [0]' to 'int' в вызове диапазона позволяет векторизовать. – antony