2016-03-26 2 views
0

Я пытаюсь распараллеливание следующего кода, который включает в себя множество Numpy операций массиваКак распараллелить Numpy операции в Cython

#fft_fit.pyx 
    import cython 
    import numpy as np 
    cimport numpy as np 
    from cython.parallel cimport prange 
    from libc.stdlib cimport malloc, free 

    dat1 = np.genfromtxt('/home/bagchilab/Sumanta_files/fourier_ecology_sample_data_set.csv',delimiter=',') 
    dat = np.delete(dat1, 0, 0) 
    yr = np.unique(dat[:,0]) 
    fit_dat = np.empty([1,2]) 


    def fft_fit_yr(np.ndarray[double, ndim=1] yr, np.ndarray[double, ndim=2] dat, int yr_idx, int pix_idx): 
     cdef np.ndarray[double, ndim=2] yr_dat1 
     cdef np.ndarray[double, ndim=2] yr_dat 
     cdef np.ndarray[double, ndim=2] fft_dat 
     cdef np.ndarray[double, ndim=2] fft_imp_dat 
     cdef int len_yr = len(yr) 
     for i in prange(len_yr ,nogil=True): 
      with gil: 

       yr_dat1 = dat[dat[:,yr_idx]==yr[i]] 
       yr_dat = yr_dat1[~np.isnan(yr_dat1).any(axis=1)] 
       print "index" ,i 
       y_fft = np.fft.fft(yr_dat[:,pix_idx]) 
       y_fft_abs = np.abs(y_fft) 
       y_fft_freq = np.fft.fftfreq(len(y_fft), 1) 
       x_fft = range(len(y_fft)) 
       fft_dat = np.column_stack((y_fft, y_fft_abs)) 
       cut_off_freq = np.percentile(y_fft_abs, 25) 
       imp_freq = np.array(y_fft_abs[y_fft_abs > cut_off_freq]) 
       fft_imp_dat = np.empty((1,2)) 
     for j in range(len(imp_freq)): 
        freq_dat = fft_dat[fft_dat[:, 1]==imp_freq[j]] 
        fft_imp_dat = np.vstack((fft_imp_dat , freq_dat[0,:]))  
       fft_imp_dat = np.delete(fft_imp_dat, 0, 0) 
       fit_dat1 = np.fft.ifft(fft_imp_dat[:,0]) 
       fit_dat2 = np.column_stack((fit_dat1.real, [yr[i]] * len(fit_dat1))) 
       fit_dat = np.concatenate((fit_dat, fit_dat2), axis = 0) 

Я использовал следующий код для setup.py

####setup.py 
    from distutils.core import setup 
    from distutils.extension import Extension 
    from Cython.Distutils import build_ext 

    setup(
cmdclass = {'build_ext': build_ext}, 
ext_modules = [Extension("fft_fit_yr", ["fft_fit.pyx"])] 
    extra_compile_args=['-fopenmp'], 
    extra_link_args=['-fopenmp'])] 
    ) 

Но Я получаю следующую ошибку при компиляции fft_fit.pyx в cython:

for i in prange(len_yr ,nogil=True): 
    target may not be a Python object as we don't have the GIL 

Ple ase сообщите мне, где я ошибаюсь при использовании функции prange. Спасибо.

+0

Три точки: 1) конкретное сообщение об ошибке, которое вы получаете, вероятно, потому, что вы не 'cdef'ed' i' должны быть целыми числами. 2) отлично использовать 'с gil:' в 'prange' для небольших кусков, но эти части не могут выполняться параллельно, поэтому совершенно бессмысленно иметь _whole_ вещь в' with gil: '. 3) ali_m прав - материал, который вы делаете, не будет работать в разделе 'prange'. Вероятнее всего, ваш лучший выбор. – DavidW

+0

Спасибо за ваши комментарии! Было бы очень полезно, если вы сообщите мне, как использовать многопроцессорность в этом коде. –

ответ

2

Вы не можете (по крайней мере, не использовать Cython).

Функции Numpy работают с объектами Python и поэтому требуют GIL, что предотвращает параллельное выполнение нескольких собственных потоков. Если вы скомпилируете свой код с помощью cython -a, вы получите аннотированный HTML-файл, который показывает, где выполняются вызовы Python C-API (и поэтому, когда GIL не может быть выпущен).

Cython наиболее полезен там, где у вас есть определенное узкое место в коде, которое нелегко ускорить с помощью векторизации. Если ваш код уже проводит большую часть времени в вызовах функции numpy, то вызов этих одних и тех же функций из Cython не приведет к существенному повышению производительности. Чтобы увидеть заметную разницу, вам нужно будет написать некоторые или все ваши операции с массивом как явные for. Однако мне кажется, что есть намного более простые оптимизации, которые могут быть сделаны для вашего кода.

Я предлагаю вам сделать следующее:

  1. Профиль исходный код Python (например, с использованием line_profiler), чтобы увидеть, где узкие места.
  2. Обратите внимание на ускорение этих узких мест в версии с одной резьбой. Вы должны задать отдельный вопрос о SO, если вам нужна помощь в этом.
  3. Если оптимизированная однопоточная версия по-прежнему слишком медленна для ваших нужд, расставьте ее с помощью joblib или multiprocessing. Параллелизация обычно равна последнему инструменту, чтобы достичь того, как вы уже пробовали все остальное, о чем можете подумать.
+0

Спасибо за ваши комментарии и объяснение по умолчанию в моем коде. Я попытаюсь оптимизировать этот код в одном потоке. Но было бы очень полезно, если бы вы могли направить меня на любые аналогичные коды или подсказки, где распараллеливание выполняется на нескольких операциях массива numpy. –

+0

Это гораздо более полезно для будущих читателей, если уделить внимание вашим вопросам - было бы лучше задать отдельный вопрос, если вы хотите помочь в оптимизации вашей однопоточной версии. Как я уже упоминал выше, 'joblib' является одним из самых простых методов для распараллеливания вызовов функций numpy.Это в основном удобная обертка вокруг встроенного модуля «многопроцессорный» Python. Вы должны посмотреть на документацию, на которую я ссылаюсь в своем ответе, на некоторые примеры, показывающие, как ее использовать. –

Смежные вопросы