Почему cffi намного быстрее, чем numpy?

Я играл с написанием модулей cffi на python, и их скорость заставляет меня задаться вопросом, правильно ли я использую стандартный python. Это заставляет меня хотеть полностью перейти на C! По правде говоря, есть несколько замечательных библиотек Python, которые я никогда не смог бы переписать на C, так что это скорее гипотетически, чем что-либо на самом деле.

В этом примере показано, как функция sum в python используется с массивом numpy, и насколько она медленная по сравнению с функцией c. Есть ли более быстрый питонический способ вычисления суммы массива numpy?

def cast_matrix(matrix, ffi):
    ap = ffi.new("double* [%d]" % (matrix.shape[0]))
    ptr = ffi.cast("double *", matrix.ctypes.data)
    for i in range(matrix.shape[0]):
        ap[i] = ptr + i*matrix.shape[1]                                                                
    return ap 

ffi = FFI()
ffi.cdef("""
double sum(double**, int, int);
""")
C = ffi.verify("""
double sum(double** matrix,int x, int y){
    int i, j; 
    double sum = 0.0;
    for (i=0; i<x; i++){
        for (j=0; j<y; j++){
            sum = sum + matrix[i][j];
        }
    }
    return(sum);
}
""")
m = np.ones(shape=(10,10))
print 'numpy says', m.sum()

m_p = cast_matrix(m, ffi)

sm = C.sum(m_p, m.shape[0], m.shape[1])
print 'cffi says', sm

просто чтобы показать, что функция работает:

numpy says 100.0
cffi says 100.0

теперь, если я выполню эту простую функцию, я обнаружу, что numpy работает очень медленно! Правильно ли я использую numpy? Есть ли более быстрый способ вычислить сумму в python?

import time
n = 1000000

t0 = time.time()
for i in range(n): C.sum(m_p, m.shape[0], m.shape[1])
t1 = time.time()

print 'cffi', t1-t0

t0 = time.time()
for i in range(n): m.sum()
t1 = time.time()

print 'numpy', t1-t0

раз:

cffi 0.818415880203
numpy 5.61657714844

person Tim    schedule 14.04.2014    source источник
comment
Используйте модуль timeit для сравнительного анализа. Если у вас установлен ipython, попробуйте %timeit np.sum(np.sum(m)) и `%timeit np.matrix.sum(x)` сборка мусора и т. д. может быть проблемой   -  person Fredrik Pihl    schedule 14.04.2014
comment
Вероятно, в основном это происходит из-за накладных расходов Python, попробуйте это с большими массивами, скажем, 1E3x1E3, и уменьшите количество циклов, чтобы увидеть гораздо более сопоставимые времена.   -  person Daniel    schedule 14.04.2014


Ответы (1)


Numpy медленнее, чем C, по двум причинам: накладные расходы Python (вероятно, похожие на cffi) и универсальность. Numpy предназначен для работы с массивами произвольных размеров в наборе различных типов данных. Ваш пример с cffi был сделан для двумерного массива поплавков. Стоимость заключалась в том, чтобы написать несколько строк кода вместо .sum(), 6 символов, чтобы сэкономить менее 5 микросекунд. (Но вы, конечно, это уже знали). Я просто хочу подчеркнуть, что процессорное время дешево, намного дешевле, чем время разработчика.

Теперь, если вы хотите использовать Numpy и повысить производительность, лучше всего использовать Bttleneck. . Они предоставляют несколько функций, оптимизированных для одномерных и двумерных массивов чисел с плавающей запятой и двойных чисел, и работают молниеносно. В вашем случае в 16 раз быстрее, что сократит время выполнения до 0,35 или примерно в два раза быстрее, чем cffi.

Для других функций, которых нет в узком месте, вы можете использовать Cython. Это помогает вам писать код C с более питоническим синтаксисом. Или, если хотите, постепенно конвертируйте Python в C, пока не будете довольны скоростью.

person Davidmh    schedule 14.04.2014
comment
Обратите внимание, что если вы используете специализированную функцию узкого места напрямую, скорость увеличивается до ~ 25 раз по сравнению с Numpy. Где твой cffi сейчас? :) - person Davidmh; 14.04.2014
comment
Помимо операций с nan, это не намного быстрее, чем сумма numpy. Ключевым моментом здесь, по-видимому, является избежание накладных расходов на Python за счет предварительного выбора базовой функции C. - person Daniel; 14.04.2014
comment
Без Nans я получаю скорость от 16 до 25x. Предоставляет ли Numpy специализированные функции? Вы всегда можете вызвать их из C API, но вам нужно пройти через Cython. - person Davidmh; 15.04.2014
comment
Кроме того, касса numba. - person meawoppl; 06.11.2014