2013-12-19 25 views
7

私は最近numbaにつまずいて、手作りのC拡張をより洗練された自動ピットコードに置き換えることを考えました。残念ながら、私は最初の、迅速なベンチマークを試みたとき、私は満足していませんでした。numbaのnumpy配列へのアクセスを最適化

10 loops, best of 3: 52 ms per loop 
10 loops, best of 3: 42.2 ms per loop 
10 loops, best of 3: 43.5 ms per loop 
1000 loops, best of 3: 321 us per loop 

私が実行している:これは私のマシン上で与え

from numba import jit, autojit, uint, double 
import numpy as np 
import imp 
import logging 
logging.getLogger('numba.codegen.debug').setLevel(logging.INFO) 

def sum_accum(accmap, a): 
    res = np.zeros(np.max(accmap) + 1, dtype=a.dtype) 
    for i in xrange(len(accmap)): 
     res[accmap[i]] += a[i] 
    return res 

autonumba_sum_accum = autojit(sum_accum) 
numba_sum_accum = jit(double[:](int_[:], double[:]), 
         locals=dict(i=uint))(sum_accum) 

accmap = np.repeat(np.arange(1000), 2) 
np.random.shuffle(accmap) 
accmap = np.repeat(accmap, 10) 
a = np.random.randn(accmap.size) 

ref = sum_accum(accmap, a) 
assert np.all(ref == numba_sum_accum(accmap, a)) 
assert np.all(ref == autonumba_sum_accum(accmap, a)) 

%timeit sum_accum(accmap, a) 
%timeit autonumba_sum_accum(accmap, a) 
%timeit numba_sum_accum(accmap, a) 

accumarray = imp.load_source('accumarray', '/path/to/accumarray.py') 
assert np.all(ref == accumarray.accum(accmap, a)) 

%timeit accumarray.accum(accmap, a) 

:numbaがここで普通のpythonよりもはるかに良いやっていないように私はほぼCのようなパフォーマンスを期待しているだろうけれどもそれは、そうですpypiから最新のnumbaバージョン、0.11.0。任意の提案、どのようにコードを修正するため、それはnumbaと合理的に高速で実行されますか?

答えて

4
@autojit 
def numbaMax(arr): 
    MAX = arr[0] 
    for i in arr: 
     if i > MAX: 
      MAX = i 
    return MAX 

@autojit 
def autonumba_sum_accum2(accmap, a): 
    res = np.zeros(numbaMax(accmap) + 1) 
    for i in xrange(len(accmap)): 
     res[accmap[i]] += a[i] 
    return res 

10 loops, best of 3: 26.5 ms per loop <- original 
100 loops, best of 3: 15.1 ms per loop <- with numba but the slow numpy max 
10000 loops, best of 3: 47.9 µs per loop <- with numbamax 
6

私は自分自身を考え出しました。 numbaは、accmapの型がintに設定されていても、np.max(accmap)の結果の型を判別できませんでした。これは、何らかの形ですべてを遅くしますが、修正は簡単です:

@autojit(locals=dict(reslen=uint)) 
def sum_accum(accmap, a): 
    reslen = np.max(accmap) + 1 
    res = np.zeros(reslen, dtype=a.dtype) 
    for i in range(len(accmap)): 
     res[accmap[i]] += a[i] 
    return res 

結果は、Cバージョンの2/3程度、非常に印象的である:純粋なpythonのための最も効果的な

10000 loops, best of 3: 192 us per loop 
+0

numbaがありますコード、およびnumpy配列。最適に加速することができない特別な機能。純粋なPythonで独自のmax関数を書いて、それを自動化すると、おそらくもっと速くなるでしょう。 – M4rtini

+2

私はnumbaのコードを調べました。多くの数値関数の場合、入力を見て関数の動作を知ることで出力データ型を正しく決定します。型推論と呼ばれています。例: 'np.sum'と' np.prod'を使用しますが、minとmaxはコードにリストされていません。私はバグレポートを提出しました。 – Michael