cython

2016-03-26 15 views
0

私は、多くの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のために次のコードを使用しているが含まれ、次のコードを並列化しようとしている中でnumpyの操作を並列化する方法を

####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私はprange関数を使用している間、どこに間違っているのかを教えてください。おかげさまで

+0

3つのポイント:1)特定のエラーメッセージは、おそらくあなたが 'cdef 'で' i'を整数にしていないためです。 2)小さな塊のために 'prange'で' with gil: 'を使うのはいいですが、それらの部分は並行して実行することはできませんので' 'gil:'で ''全体を持つことは全く意味がありません。 3)ali_mが正しい - あなたがやっているものは 'prange'セクションでは動作しません。 mutliprocessingはおそらくあなたの最善の策です。 – DavidW

+0

あなたのコメントをありがとう!このコードでマルチプロセスを使用する方法を教えていただければ大きな助けになります。 –

答えて

2

(少なくともCythonは使用しないでください)できません。

Numpy関数はPythonオブジェクトを操作するため、複数のネイティブスレッドが並列に実行されないようにするためにGILが必要です。 cython -aを使用してコードをコンパイルすると、Python C-API呼び出しがどこで行われているか(したがってGILをリリースできない場所)を示す注釈付きHTMLファイルが得られます。

Cythonは、コード化に特有のボトルネックがあり、ベクトル化を使用して簡単に高速化できない場合に最も便利です。あなたのコードがnumpy関数呼び出しですでにほとんどの時間を費やしているのであれば、Cythonからまったく同じ関数を呼び出しても、パフォーマンスの大幅な向上は起こりません。顕著な違いを確認するには、配列操作の一部またはすべてを明示的なforループとして記述する必要があります。しかし、あなたのコードに行うことができるより簡単な最適化があるように見えます。

私は、あなたが次の操作を行うことを示唆している:

  1. は、ボトルネックがどこにあるか確認するために(例えばline_profiler使用して)元のPythonコードのプロファイルを作成します。
  2. シングルスレッドのバージョンでは、これらのボトルネックを高速化することに注意してください。もしあなたがこれを助けたいならば、別の質問をする必要があります。
  3. 最適化されたシングルスレッドバージョンが依然として必要以上に遅い場合は、joblibまたはmultiprocessingを使用して並列化します。並列化は通常、の最後ののツールであり、一度あなたが考えていることをもう一度試してみることができます。
+0

あなたのコメントと、私のコードのデフォルトを説明してくれてありがとう。このコードをシングルスレッドで最適化しようとします。しかし、並列化が複数の配列操作で実行されている同様のコードやヒントに私を導くことができれば、大きな助けになるでしょう。 –

+0

あなたの質問に焦点を当てておくと、将来の読者にとってははるかに役立ちます。シングルスレッド版を最適化するための助けが必要な場合は、別の質問をすることをお勧めします。上で述べたように、 'joblib'はnumpy関数呼び出しを並列化する最も簡単な方法の1つです。これは、基本的に、 'マルチプロセッシング' Python組み込みモジュールに関する便利なラッパーです。私の答えにリンクしている文書を見て、その使い方の例をいくつか見てください。 –