2013-08-06 7 views
11

न्यूम्बा संख्यात्मक कोड के निष्पादन में तेजी लाने के लिए एक अच्छा समाधान प्रतीत होता है। हालांकि, जब एक सरणी के लिए असाइनमेंट होते हैं तो मानक पाइथन कोड से धीमा लगता है। इस उदाहरण पर विचार करें, बिना किसी निंबा/स्केलर के चार विकल्पों की तुलना में,एक सरणी को आवंटित करते समय Numba धीमा?

(इस मुद्दे पर ध्यान केंद्रित करने के लिए गणना को उद्देश्य पर बहुत सरल रखा गया था, जो एक सरणी के लिए एक स्केलर बनाम असाइनमेंट के लिए असाइनमेंट है सेल)

@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 

IPython के% timeit का उपयोग करते हुए चार विकल्प मुझे मिल गया मूल्यांकन करने के लिए:

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, जो Numba साथ संकलित नहीं किया गया है दोगुनी गति से fast_sum_arr, के रूप में की तुलना में अधिक है जो नुंबा के साथ संकलित किया गया था। दूसरी तरफ, fast_sum_sclr, जो नुम्बा के साथ संकलित था, sum_sclr की तुलना में तीव्रता के दो से अधिक आदेश है, जिसे नुंबा के साथ संकलित नहीं किया गया था।

तो नुंबा sum_sclr को तेज़ करने का कार्य उल्लेखनीय रूप से अच्छी तरह से करता है लेकिन वास्तव में sum_arr धीमा निष्पादित करता है। Sum_sclr और sum_arr के बीच एकमात्र अंतर यह है कि पूर्व स्केलर को असाइन करता है जबकि बाद वाला सरणी सेल को असाइन करता है।

अगर कोई संबंध है मैं नहीं जानता, लेकिन मैं हाल ही में ब्लॉग http://www.phi-node.com/ पर निम्न को पढ़ें:

"ऐसा लगता है कि जब Numba किसी भी निर्माण के साथ सामना कर रहा है यह सीधे समर्थन नहीं करता है, यह एक (बहुत) धीमी कोड पथ पर स्विच करता है। "

ब्लॉग लेखक को पाइथन के अधिकतम() के बजाय एक कथन का उपयोग करके नुंबा को बहुत तेज़ प्रदर्शन करने के लिए मिला।

इस पर कोई अंतर्दृष्टि?

धन्यवाद,

एफएस

+2

मुझे समझ में नहीं आता कि आपका लूप क्या कर रहा है। क्या यह प्रभावी रूप से 'z [1:] + = arr [1:]' नहीं है, या चूंकि 'z' और' r' के समान मान हैं, 'z [1:] * = 2'? मैं उम्मीद करता हूं कि किसी स्पष्ट लूप की तुलना में बहुत तेज हो, लेकिन मुझे उम्मीद नहीं है कि एक कंपाइलर बताने में सक्षम हो। – Blckknght

उत्तर

1

मैं Numba के बारे में ज्यादा पता नहीं है, लेकिन यदि हम यह क्या हुड के नीचे कर रहा है के बारे में कुछ बुनियादी मान्यताओं हम अनुमान लगा सकते हैं क्यों autojit संस्करण धीमी और कैसे लाने के लिए है मामूली बदलाव के साथ इसे ...

के sum_arr साथ शुरू करते हैं,

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 

सुंदर स्पष्ट यहाँ क्या हो रहा है, लेकिन लाइन 5 जो ca के बारे में चुनने देती हैं n

के रूप में लिखा जा
1 a = arr[i] 
2 b = z[i] 
3 c = a + b 
4 z[i] = c 

अजगर आगे इस

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

एक, बी और सी के रूप में inturpet जाएगा numpy.int64 (या समान) के सभी उदाहरणों हैं

मुझे लगता है कि Numba कोशिश कर रहा है इन वस्तुओं के दिनांक प्रकार का निरीक्षण करने के लिए और उन्हें कुछ numba देशी डेटाटाइप में परिवर्तित करें (मुझे सबसे खराब धीमी गति में से एक को निष्क्रिय कोड के साथ देखा जाता है अनजाने में पाइथन डेटाटाइप से numpy डेटाटाइप तक स्विचिंग)। यदि यह वास्तव में हो रहा है, तो numba कम से कम 3 रूपांतरण कर रहा है, 2 numpy.int64 -> मूल, 1 मूल -> numpy.int64, या शायद मध्यवर्ती के साथ बदतर (numpy.int64 -> पायथन int -> मूल (सी पूर्णांक))। मुझे संदेह है कि numba डेटाटाइप की जांच में अतिरिक्त ओवरहेड जोड़ देगा, शायद लूप को अनुकूलित नहीं करेगा।चलो देखते हैं अगर हम पाश से प्रकार परिवर्तन को दूर क्या होता है ...

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 पर सूक्ष्म परिवर्तन, प्रति के बजाय tolist, अजगर ints को डेटाप्रकार बदलता है, लेकिन हम अभी भी एक numpy.int64 है -> लाइन 6. पर देशी की है कि फिर से लिखने दो, z [i] + = z [i]

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) 
सभी परिवर्तनों को हम एक बहुत बड़ा speedup देख (हालांकि यह जरूरी शुद्ध अजगर को हरा नहीं करता है) के साथ

। बेशक, एआर + एआर, बस बेवकूफ तेज है।

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) 

और समय,

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

दिलचस्प अंतर्दृष्टि। मेरे परीक्षण में मुझे – Soldalma

+0

दिलचस्प अंतर्दृष्टि मिली। मेरे परीक्षण में मैं noncompiled समारोह अभी भी संकलित एक धड़कता है। अंतर बहुत महत्वपूर्ण नहीं है। स्केलर मामले (fast_sum_sclr) में त्वरण और वेक्टर केस (fast_sum_arrX) में त्वरण के बीच हड़ताली अंतर अस्पष्ट रहता है। – Soldalma

3

यहाँ क्या धीमी है arr.copy() फ़ंक्शन, नहीं एक सरणी के लिए लेखन पहुँच है। सबूत:

# -*- 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

** ऑप्टिकल भ्रम। ** तथ्य यह है कि यह तेजी से इस तथ्य पर निर्भर करता है कि numba आलसी प्रारंभिक उपयोग करता है। यह पहली बार धीमा है जब आप उसी सत्र में फ़ंक्शन को कॉल करते हैं, और उसके बाद दूसरे से 10000 वें समय तक तेज़ होते हैं। इसलिए पहला धीमा, औसत में गायब हो जाता है। इसे 10000 के बजाय 1 के साथ कॉल करने का प्रयास करें, और इसे फ़ंक्शन के अंदर कॉपी के साथ कॉल करने का प्रयास करें। आप देखेंगे कि कॉपी() की स्थिति एक बाधा नहीं है। – SeF

0

हां, नुंबा आलसी प्रारंभिकता का उपयोग करता है, इसलिए दूसरी बार जब आप इसे कॉल करते हैं तो यह तेज़ होता है। आलसी प्रारंभिकरण के बावजूद बड़े सरणी के साथ, numba अभी भी नो-numba से बेहतर है।

अजगर 3 के साथ निम्नलिखित uncommenting विभिन्न ख

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)) 

प्रयास करें अपने सिस्टम पर, Numba 0.34.0 यह हो जाता है

""" 
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 
""" 

और

""" 
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 
""" 

और

यह देखने में दिलचस्प है कि पहले कॉल के बीच कम्प्यूटेशनल समय में अंतर और दूसरा एक सरणी आकार में वृद्धि को कम करता है। मुझे नहीं पता कि यह वास्तव में ऐसा क्यों काम करता है।

संबंधित मुद्दे