Почему Cffi намного быстрее, чем NumPy?
Я занимался написанием модулей cffi на python, и их скорость заставляет задуматься, правильно ли я использую стандартный python. Это заставляет меня хотеть полностью переключиться на C! По правде говоря, есть несколько замечательных библиотек Python, которые я никогда не смогу переопределить в C, так что это более гипотетично, чем что-либо на самом деле.
В этом примере показана функция суммы в python, которая используется с массивом numpy, и насколько она медленная по сравнению с функцией ac. Есть ли более быстрый питонический способ вычисления суммы в массиве 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? Есть ли более быстрый способ подсчета суммы в питоне?
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
1 ответ
Numpy медленнее, чем C, по двум причинам: накладные расходы Python (вероятно, аналогичные cffi) и общность. Numpy предназначен для работы с массивами произвольных измерений в группе разных типов данных. Ваш пример с cffi был сделан для двумерного массива с плавающей точкой. Стоимость написания нескольких строк кода против .sum()
, 6 символов, чтобы сохранить менее 5 микросекунд. (Но, конечно, вы уже знали это). Я просто хочу подчеркнуть, что процессорное время дешево, намного дешевле, чем время разработчика.
Теперь, если вы хотите придерживаться Numpy и хотите повысить производительность, лучше всего использовать Bottleneck. Они предоставляют несколько функций, оптимизированных для 1 и 2D массивов с плавающей и двойной частями, и они работают быстро. В вашем случае это в 16 раз быстрее, что увеличивает время выполнения на 0,35, или примерно в два раза быстрее, чем cffi.
Для других функций, которые не имеют узкого места, вы можете использовать Cython. Это поможет вам написать код на C с более питоническим синтаксисом. Или, если хотите, постепенно конвертируйте Python в C, пока вы не будете довольны скоростью.