2017-05-10 4 views
7

मेरे पास लॉग फ़ंक्शन (पूर्णांक के लिए), अरबों कॉल के भारी उपयोग के साथ एक गणना परियोजना है। मुझे लगता है कि numpy के लॉग का प्रदर्शन आश्चर्यजनक रूप से धीमा है।numpy के लॉग फ़ंक्शन में क्या होता है? प्रदर्शन में सुधार करने के तरीके हैं?

निम्नलिखित कोड 15 से 17 सेकेंड लगते हैं पूरा करने के लिए:

import numpy as np 
import time 

t1 = time.time() 
for i in range(1,10000000): 
    np.log(i) 
t2 = time.time() 
print(t2 - t1) 

हालांकि, math.log समारोह 3 से 4 सेकंड से बहुत कम समय लगता है।

import math 
import time 

t1 = time.time() 
for i in range(1,10000000): 
    math.log(i) 
t2 = time.time() 
print(t2 - t1) 

मैंने मैटलैब और सी # का भी परीक्षण किया, जिसमें क्रमशः 2 सेकंड और केवल 0.3 सेकंड लगते हैं।

matlab

tic 
for i = 1:10000000 
    log(i); 
end 
toc 

सी #

var t = DateTime.Now; 
for (int i = 1; i < 10000000; ++i) 
    Math.Log(i); 
Console.WriteLine((DateTime.Now - t).TotalSeconds); 

वहाँ अजगर में किसी भी तरह से है कि मैं लॉग समारोह के प्रदर्शन में सुधार कर सकते हैं?

+0

मुझे उत्तर में बहुत दिलचस्पी है। विशेष रूप से क्योंकि मैं अभी स्रोत कोड में गया था, और फ़ंक्शन का पता लगाने में मुश्किल है। इसके बाईं ओर स्रोत कोड टिन करें, यह लिखा गया है: '# वास्तविक हस्ताक्षर अज्ञात; __doc__' से बहाल क्या कोई यह भी समझा सकता है कि स्रोत कोड कैसे काम करता है? –

+1

उत्तर यहां दिखाई देता है http://stackoverflow.com/questions/3650194/are-numpys-math-functions-faster-than-pythons?rq=1 – Bathsheba

+9

'np.log' को सरणी पर काम करने के लिए अनुकूलित किया गया है मान, एकल मूल्य नहीं। उदाहरण के लिए 'np.log (np.arange (1,10000000))' (उस श्रेणी में पूर्णांक की सरणी का लॉग) मेरे लिए लगभग 120ms लेता है। –

उत्तर

2

NumPys फ़ंक्शंस को एकल मानों या स्केलर के लिए नहीं सरणी के लिए डिज़ाइन किया गया है। उनके पास एक उच्च ओवरहेड है क्योंकि वे कई चेक और रूपांतरण करते हैं जो बड़े सरणी के लिए गति लाभ प्रदान करेंगे लेकिन ये स्केलर्स के लिए महंगा हैं।

>>> import numpy as np 
>>> import math 

>>> type(np.log(2.)) 
numpy.float64 
>>> type(math.log(2.)) 
float 

दूसरी ओर math मॉड्यूल scalars के लिए अनुकूलित है पर:

रूपांतरण अगर आप वापसी के प्रकार की जाँच वास्तव में स्पष्ट है। इसलिए उन्हें कई चेक की आवश्यकता नहीं है (मुझे लगता है कि केवल दो हैं: float पर कनवर्ट करें और जांचें कि यह <= 0 है)। math.log की तुलना में math.log तेजी से स्केलर के लिए है।

लेकिन यदि आप सरणी पर काम करते हैं और सरणी में सभी तत्वों के लॉगरिदम लेना चाहते हैं तो NumPy बहुत तेज़ हो सकता है। अपने कंप्यूटर पर अगर मैं एक सूची में प्रत्येक आइटम का math.log की तुलना में एक सरणी पर np.log का निष्पादन समय तो समय अलग दिखता है:

arr = np.arange(1, 10000000) 
%timeit np.log(arr) 
201 ms ± 959 µs per loop (mean ± std. dev. of 7 runs, 1 loop each) 

lst = arr.tolist() 
%timeit [math.log(item) for item in lst] 
8.77 s ± 63.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 

तो np.log परिमाण के कई क्रमों तेजी सरणियों पर होगा (यह से भी अधिक है इस मामले में 40 गुना तेजी से)! और आपको खुद को कोई लूप लिखने की जरूरत नहीं है। चूंकि ufunc np.log भी बहुआयामी numpy arrays पर सही ढंग से काम करेगा और ऑपरेशन को जगह में भी करने की अनुमति देता है।

अंगूठे के नियम के रूप में: यदि आपके पास हजारों आइटमों के साथ एक सरणी है तो यदि आपके पास स्केलर हैं या केवल कुछ दर्जन आइटम math + स्पष्ट लूप तेज होंगे।


भी समय कोड के लिए time का उपयोग न करें। समय के दौरान समर्पित मॉड्यूल है कि अधिक सटीक परिणाम देने के लिए, बेहतर आंकड़े और अक्षम कचरा संग्रहण के होते हैं:

  • timeit (बिल्ट-इन)
  • perf (विस्तार पैकेज)

मैं आम तौर पर %timeit का उपयोग जो timeit कार्यक्षमता के आसपास एक सुविधाजनक रैपर है, लेकिन इसे IPython की आवश्यकता है। वे पहले से ही आसानी से परिणाम और विचलन प्रदर्शित करते हैं और कुछ (अधिकतर) उपयोगी आंकड़े करते हैं जैसे कि "सर्वश्रेष्ठ 7" या "सर्वश्रेष्ठ 3" परिणाम प्रदर्शित करना।


मैं हाल ही में another question के लिए numpy कार्यों का क्रम व्यवहार का विश्लेषण किया, अंक में से कुछ भी यहाँ लागू होते हैं।

1

दिलचस्प बात यह है कि पाइथन मानक लाइब्रेरी धीमा होने का मुद्दा मेरी मशीन (विंडोज 10, पाइथन 2.7.11 और numpy 1.11.0 चल रहा है) पर दोहराना नहीं है।

>>> t1 = time.time() 
>>> for i in range(1,10000000): 
>>>  _ = np.log(i) 
>>> t2 = time.time() 
>>> print(t2 - t1) 
9.86099982262 
>>> t1 = time.time() 
>>> for i in range(1,10000000): 
>>>  _ = math.log(i) 
>>> t2 = time.time() 
>>> print(t2 - t1) 
2.48300004005 

Matlab में आपके प्रदर्शन के समान। @Nils एक अच्छा मुद्दा उठाता है, हालांकि, numpy को सरणी पर कुशल होने के लिए डिज़ाइन किया गया है।

>>> t1 = time.time() 
>>> for i in range(1,1000): 
>>>  _ = np.log(np.arange(1,10000)) 
>>> t2 = time.time() 
>>> print(t2 - t1) 
0.146000146866 
>>> t1 = time.time() 
>>> for i in range(1,1000): 
>>>  _ = [math.log(i) for i in range(1,10000)] 
>>> t2 = time.time() 
>>> print(t2 - t1) 
2.3220000267 

आप अपने इनपुट vectorize कर सकते हैं, numpy मानक गणित पुस्तकालय मात और यहां तक ​​कि सी # के करीब आ जाएगा।

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