2013-08-06 19 views
11

Numba, sayısal kodun yürütülmesini hızlandırmak için harika bir çözüm gibi görünüyor. Bununla birlikte, bir Numba dizisine atamalar olduğunda standart Python kodundan daha yavaş görünür. Bu örneği, Numba ile birlikte/olmadan dört alternatifi karşılaştırarak bir dizi/skaler:Numba yavaş mı?

(Hesaplamalar, bir diziye atama veya öbeğe atamak olan soruna odaklanmak için çok basit tutuldu. bende dört alternatifleri değerlendirmek üzere ipython en% sürümüyle gelen timeit kullanarak hücre)

@autojit 
def fast_sum_arr(arr): 
    z = arr.copy() 
    M = len(arr) 
    for i in range(M): 
     z[i] += arr[i] 

    return z 

def sum_arr(arr): 
    z = arr.copy() 
    M = len(arr) 
    for i in range(M): 
     z[i] += arr[i] 

    return z 

@autojit 
def fast_sum_sclr(arr): 
    z = 0 
    M = len(arr) 
    for i in range(M): 
     z += arr[i] 

    return z 

def sum_sclr(arr): 
    z = 0 
    M = len(arr) 
    for i in range(M): 
     z += arr[i] 

    return z 

: Numba ile derlenmiş değildi olduğu

In [125]: %timeit fast_sum_arr(arr) 
100 loops, best of 3: 10.8 ms per loop 

In [126]: %timeit sum_arr(arr) 
100 loops, best of 3: 4.11 ms per loop 

In [127]: %timeit fast_sum_sclr(arr) 
100000 loops, best of 3: 10 us per loop 

In [128]: %timeit sum_sclr(arr) 
100 loops, best of 3: 2.93 ms per loop 

sum_arr, iki kat daha hızlı fast_sum_arr olarak daha hangi Numba ile derlenmiştir. Öte yandan, , Numba ile derlenmiş olan olan fast_sum_sclr, Sumba ile derlenmemiş olan sum_sclr'den iki kat büyüklükte daha hızlıdır.

Numba, sum_sclr'ı hızlandırma görevini dikkat çekici derecede iyi gerçekleştirir, ancak aslında sum_arr'ın daha yavaş çalışmasını sağlar. Sum_sclr ve sum_arr arasındaki tek fark, birincinin bir dizi hücresine atarken bir skaler atamasıdır.

herhangi bir ilişki olup olmadığını bilmiyorum, ama son zamanlarda blogda http://www.phi-node.com/ aşağıdaki okuyun:

"Bu Numba herhangi bir yapı ile karşı karşıya olduğunda doğrudan desteklemediği çıkıyor, bunu (çok) yavaş kod yoluna geçer. "

Blog yazarı, Python'un max() yerine bir if deyimini kullanarak çok daha hızlı gerçekleştirmek için Numba'yı aldı.

Bu konuyla ilgili herhangi bir bilgi var mı?

sayesinde

FS

+2

Döngünün ne yapması gerektiğini anlamıyorum. Bu etkili bir şekilde değil mi? Z [1:] + = arr [1:] ', ya da' z' ve 'r' aynı değerlere sahip olduğundan, z [1:] * = 2'? Bunun açık bir döngüden çok daha hızlı olmasını beklerdim, ancak bir derleyicinin anlatabileceğini sanmam. – Blckknght

cevap

1

Ben Numba hakkında çok şey bilmiyorum ama biz başlık altında ne yaptığını hakkında bazı temel varsayımlar yaparsanız autojit versiyonu daha yavaş ve nasıl hızlandırmak için neden biz çıkarabiliriz küçük değişikliklerle o kadar ...

, en sum_arr ile başlayalım

1 def sum_arr(arr): 
2  z = arr.copy() 
3  M = len(arr) 
4  for i in range(M): 
5   z[i] += arr[i] 
6 
7  return z 

burada oluyor ama hattı 5 ca hakkında seçelim ne çok açık N

olarak yeniden yazılabilir
1 a = arr[i] 
2 b = z[i] 
3 c = a + b 
4 z[i] = c 

Python daha

1 a = arr.__getitem__(i) 
2 b = arr.__getitem__(i) 
3 c = a.__add__(b) 
4 z.__setitem__(i, c) 

a, b ve c, bu inturpet olacak numpy.int64 (ya da benzeri) örneği olan

I numba çalışıyor şüpheli Bu öğelerin tarih türünü denetlemek ve bazı numba yerel veri türlerine dönüştürmek için (numpy koduyla gördüğüm en büyük yavaşlamalardan biri python veri türlerinden istemsiz veriye geçiştir). Bu gerçekten devam ederse, numba en az 3 dönüşüm yapıyor, 2 numpy.int64 -> yerel, 1 yerel -> numpy.int64 veya muhtemelen ara ürünlerle daha kötü (numpy.int64 -> python int -> native (c) int)). Ben numba datiplerini kontrol etmek için ek yük ekleyeceğinden şüpheliyim, muhtemelen döngüleri hiç optimize etmez.

1 @autojit 
2 def fast_sum_arr2(arr): 
3  z = arr.tolist() 
4  M = len(arr) 
5  for i in range(M): 
6   z[i] += arr[i] 
7 
8  return numpy.array(z) 

3. hatta ince bir değişiklik olan kopyanın ToList, Python ints için veri türünü değiştirir ... en biz döngüden türü değişikliğini kaldırırsanız olur görelim, ama hala bir numpy.int64 var -> hattı 6 üzerinde yerli en maddesine yeniden olsun, z [i] + = z [i] (o zorunlu olarak saf piton beat rağmen) oldukça önemli bir hızlanma bkz tüm değişiklikler ile

1 @autojit 
2 def fast_sum_arr3(arr): 
3  z = arr.tolist() 
4  M = len(arr) 
5  for i in range(M): 
6   z[i] += z[i] 
7 
8  return numpy.array(z) 

. Elbette, arr + arr, sadece aptalca hızlıdır.

1 import numpy 
    2 from numba import autojit 
    3 
    4 def sum_arr(arr): 
    5  z = arr.copy() 
    6  M = len(arr) 
    7  for i in range(M): 
    8   z[i] += arr[i] 
    9 
10  return z 
11 
12 @autojit 
13 def fast_sum_arr(arr): 
14  z = arr.copy() 
15  M = len(arr) 
16  for i in range(M): 
17   z[i] += arr[i] 
18  
19  return z 
20 
21 def sum_arr2(arr): 
22  z = arr.tolist() 
23  M = len(arr) 
24  for i in range(M): 
25   z[i] += arr[i] 
26 
27  return numpy.array(z) 
28 
29 @autojit 
30 def fast_sum_arr2(arr): 
31  z = arr.tolist() 
32  M = len(arr) 
33  for i in range(M): 
34   z[i] += arr[i] 
35   
36  return numpy.array(z) 
37  
38 def sum_arr3(arr): 
39  z = arr.tolist() 
40  M = len(arr) 
41  for i in range(M): 
42   z[i] += z[i] 
43   
44  return numpy.array(z) 
45 
46 @autojit 
47 def fast_sum_arr3(arr): 
48  z = arr.tolist() 
49  M = len(arr) 
50  for i in range(M): 
51   z[i] += z[i] 
52 
53  return numpy.array(z) 
54 
55 def sum_arr4(arr): 
56  return arr+arr 
57 
58 @autojit 
59 def fast_sum_arr4(arr): 
60  return arr+arr 
61 
62 arr = numpy.arange(1000) 

Ve zamanlamaları

,

In [1]: %timeit sum_arr(arr) 
10000 loops, best of 3: 129 us per loop 

In [2]: %timeit sum_arr2(arr) 
1000 loops, best of 3: 232 us per loop 

In [3]: %timeit sum_arr3(arr) 
10000 loops, best of 3: 51.8 us per loop 

In [4]: %timeit sum_arr4(arr) 
100000 loops, best of 3: 3.68 us per loop 

In [5]: %timeit fast_sum_arr(arr) 
1000 loops, best of 3: 216 us per loop 

In [6]: %timeit fast_sum_arr2(arr) 
10000 loops, best of 3: 65.6 us per loop 

In [7]: %timeit fast_sum_arr3(arr) 
10000 loops, best of 3: 56.5 us per loop 

In [8]: %timeit fast_sum_arr4(arr) 
100000 loops, best of 3: 2.03 us per loop 
+0

İlginç bilgiler. Testlerimde – Soldalma

+0

İlginç bilgiler edindim. Testimde, derlenmeyen işlev hala derlenmiş olanı yener. Fark çok önemli değil. Skaler durumdaki (fast_sum_sclr) hızlanma ile vektör durumu (fast_sum_arrX) arasındaki çarpıcı fark açıklanamayan bir durumdur. – Soldalma

3

Burada yavaş arr.copy() işlevi, değil bir diziye yazma erişimi olduğunu. Prova:

# -*- coding: utf-8 -*- 
from numba import autojit 
from Timer import Timer 
import numpy as np 

@autojit 
def fast_sum_arr(arr, z): 
    #z = arr.copy() 
    M = len(arr) 
    for i in range(M): 
     z[i] += arr[i] 

    return z 

def sum_arr(arr, z): 
    #z = arr.copy() 
    M = len(arr) 
    for i in range(M): 
     z[i] += arr[i] 

    return z 

@autojit 
def fast_sum_sclr(arr): 
    z = 0 
    M = len(arr) 
    for i in range(M): 
     z += arr[i] 

    return z 

def sum_sclr(arr): 
    z = 0 
    M = len(arr) 
    for i in range(M): 
     z += arr[i] 

    return z 

if __name__ == '__main__': 
    vec1 = np.ones(1000) 
    z = vec1.copy() 
    with Timer() as t0: 
     for i in range(10000): 
      pass 
    print "time for empty loop ", t0.secs 
    print 
    with Timer() as t1: 
     for i in range(10000): 
      sum_arr(vec1, z) 
    print "time for sum_arr [µs]: ", (t1.secs-t0.secs)/10000 * 1e6 
    with Timer() as t1: 
     for i in range(10000): 
      fast_sum_arr(vec1, z) 
    print "time for fast_sum_arr [µs]: ", (t1.secs-t0.secs)/10000 * 1e6 
    with Timer() as t1: 
     for i in range(10000): 
      sum_sclr(vec1) 
    print "time for sum_arr [µs]: ", (t1.secs-t0.secs)/10000 * 1e6 
    with Timer() as t1: 
     for i in range(10000): 
      fast_sum_sclr(vec1) 
    print "time for fast_sum_arr [µs]: ", (t1.secs-t0.secs)/10000 * 1e6 

""" 
time for empty loop 0.000312089920044 

time for sum_arr  [µs]: 432.02688694 
time for fast_sum_arr [µs]:  7.43598937988 
time for sum_arr  [µs]: 284.574580193 
time for fast_sum_arr [µs]:  5.74610233307 
""" 
+0

** Optik yanılsama. ** Bunun daha hızlı olması, numba'nın tembel başlatmayı kullandığı gerçeğine bağlıdır. Aynı seansta fonksiyonu ilk kez çağırdığınızda yavaştır ve daha sonra saniyeden 10000'lere kadar daha hızlıdır. Bu nedenle, birincisi daha yavaş, ortalamada kaybolur. 10000 yerine 1 ile aramayı deneyin ve aynı zamanda işlev içinde kopyayla da çağırmayı deneyin. Copy() konumunun bir darboğaz olmadığını göreceksiniz. – SeF

0

Evet, Numba tembel başlatmayı kullanır, bu yüzden ikinci kez aradığınız zaman daha hızlıdır. Büyük dizilerde, tembel başlatmalara rağmen, numba hala no-numba'dan daha iyidir.

numba 0.34.0 o alır, piton 3 ile benim sistemde aşağıdaki Uncommenting farklı b

import time 
import numpy as np 

from numba import jit, autojit 


@autojit 
def fast_sum_arr(arr): 
    z = arr.copy() 
    M = len(arr) 
    for i in range(M): 
     z[i] += arr[i] 

    return z 

def sum_arr(arr): 
    z = arr.copy() 
    M = len(arr) 
    for i in range(M): 
     z[i] += arr[i] 

    return z 

@autojit 
def fast_sum_sclr(arr): 
    z = 0 
    M = len(arr) 
    for i in range(M): 
     z += arr[i] 

    return z 

def sum_sclr(arr): 
    z = 0 
    M = len(arr) 
    for i in range(M): 
     z += arr[i] 

    return z 

b = np.arange(100) 
# b = np.arange(1000000) 
# b = np.arange(100000000) 

print('Vector of len {}\n'.format(len(b))) 

print('Sum ARR:\n') 

time1 = time.time() 
sum_arr(b) 
time2 = time.time() 
print('No numba:   {}'.format(time2 - time1)) 

time1 = time.time() 
fast_sum_arr(b) 
time2 = time.time() 
print('Numba first time: {}'.format(time2 - time1)) 

time1 = time.time() 
fast_sum_arr(b) 
time2 = time.time() 
print('Numba second time: {}'.format(time2 - time1)) 

print('\nSum SCLR:\n') 

time1 = time.time() 
sum_sclr(b) 
time2 = time.time() 
print('No numba:   {}'.format(time2 - time1)) 

time1 = time.time() 
fast_sum_sclr(b) 
time2 = time.time() 
print('Numba first time: {}'.format(time2 - time1)) 

time1 = time.time() 
fast_sum_sclr(b) 
time2 = time.time() 
print('Numba second time: {}'.format(time2 - time1)) 

deneyin

""" 
Vector of len 100 

Sum ARR: 

No numba:   7.414817810058594e-05 
Numba first time: 0.07130813598632812 
Numba second time: 3.814697265625e-06 

Sum SCLR: 

No numba:   2.6941299438476562e-05 
Numba first time: 0.05761408805847168 
Numba second time: 1.4066696166992188e-05 
""" 

Ve

""" 
Vector of len 1000000 

Sum ARR: 

No numba:   0.3144559860229492 
Numba first time: 0.07181787490844727 
Numba second time: 0.0014197826385498047 

Sum SCLR: 

No numba:   0.15929198265075684 
Numba first time: 0.05956888198852539 
Numba second time: 0.00037789344787597656 
""" 

ve

İlk arama ile ikincisi arasındaki hesaplama süresindeki farkın, dizi boyutunu arttığını görmek ilginçtir. Yine de neden böyle çalıştığını tam olarak bilmiyorum.