python - पायथन की सरणियाँ धीमी क्यों हैं?




arrays performance (2)

मुझे array.array उम्मीद थी। सूचियों की तुलना में तेजी होगी, क्योंकि सरणियाँ अनबॉक्स्ड लगती हैं।

हालाँकि, मुझे निम्न परिणाम मिले:

In [1]: import array

In [2]: L = list(range(100000000))

In [3]: A = array.array('l', range(100000000))

In [4]: %timeit sum(L)
1 loop, best of 3: 667 ms per loop

In [5]: %timeit sum(A)
1 loop, best of 3: 1.41 s per loop

In [6]: %timeit sum(L)
1 loop, best of 3: 627 ms per loop

In [7]: %timeit sum(A)
1 loop, best of 3: 1.39 s per loop

ऐसे अंतर का कारण क्या हो सकता है?


टिम पीटर्स के उत्कृष्ट उत्तर को जोड़ने के लिए, एफ़रे बफर प्रोटोकॉल को लागू करते हैं , जबकि सूचियाँ नहीं। इसका मतलब है कि, यदि आप एक C एक्सटेंशन (या नैतिक समतुल्य, जैसे साइथॉन मॉड्यूल लिखना) लिख रहे हैं, तो आप किसी ऐरे के तत्वों के साथ बहुत तेजी से काम कर सकते हैं और काम कर सकते हैं, जो पायथन कर सकता है। यह आपको काफी गति में सुधार देगा, संभवतः परिमाण के एक क्रम पर। हालाँकि, इसमें कई डाउनसाइड हैं:

  1. आप अब पायथन के बजाय C लिखने के व्यवसाय में हैं। साइथॉन इस बात को स्पष्ट करने का एक तरीका है, लेकिन यह भाषाओं के बीच कई बुनियादी अंतरों को खत्म नहीं करता है; आपको सी शब्दार्थ से परिचित होना चाहिए और यह समझना चाहिए कि यह क्या कर रहा है।
  2. PyPy का C API कुछ हद तक काम करता है , लेकिन बहुत तेज़ नहीं है। यदि आप PyPy को लक्षित कर रहे हैं, तो आपको शायद नियमित सूचियों के साथ सरल कोड लिखना चाहिए, और फिर JITTER को आपके लिए इसे अनुकूलित करने दें।
  3. सी एक्सटेंशन शुद्ध पायथन कोड की तुलना में वितरित करना कठिन है क्योंकि उन्हें संकलित करने की आवश्यकता है। संकलन वास्तुकला और ऑपरेटिंग-सिस्टम पर निर्भर करता है, इसलिए आपको यह सुनिश्चित करने की आवश्यकता होगी कि आप अपने लक्ष्य मंच के लिए संकलन कर रहे हैं।

सी एक्सटेंशन में सीधे जाने से आपके उपयोग के मामले के आधार पर, एक स्लेटेडहैमर का उपयोग एक स्वाट करने के लिए किया जा सकता है। आपको सबसे पहले NumPy जांच करनी चाहिए और यह देखना चाहिए कि आप जो भी गणित करने की कोशिश कर रहे हैं वह करने के लिए पर्याप्त शक्तिशाली है या नहीं। अगर इसे सही तरीके से इस्तेमाल किया जाए तो यह देशी पायथन से भी ज्यादा तेज होगा।


टिम पीटर्स ने जवाब दिया कि यह धीमा क्यों है, लेकिन आइए देखें कि इसे कैसे सुधारें

sum(range(...)) अपने उदाहरण sum(range(...)) चिपके हुए (आपके उदाहरण से 10 से छोटा कारक यहां मेमोरी में फिट होने के लिए):

import numpy
import array
L = list(range(10**7))
A = array.array('l', L)
N = numpy.array(L)

%timeit sum(L)
10 loops, best of 3: 101 ms per loop

%timeit sum(A)
1 loop, best of 3: 237 ms per loop

%timeit sum(N)
1 loop, best of 3: 743 ms per loop

इस तरह से बॉक्स / अनबॉक्स को सुन्न करने की जरूरत है, जिसमें अतिरिक्त ओवरहेड है। इसे तेज़ करने के लिए किसी को सी के भीतर रहना होगा:

%timeit N.sum()
100 loops, best of 3: 6.27 ms per loop

लिस्ट सॉल्यूशन से लेकर सिम्पी वर्जन तक रनटाइम में यह एक फैक्टर 16 है।

आइए यह भी जांचें कि उन डेटा संरचनाओं को बनाने में कितना समय लगता है

%timeit list(range(10**7))
1 loop, best of 3: 283 ms per loop

%timeit array.array('l', range(10**7))
1 loop, best of 3: 884 ms per loop

%timeit numpy.array(range(10**7))
1 loop, best of 3: 1.49 s per loop

%timeit numpy.arange(10**7)
10 loops, best of 3: 21.7 ms per loop

स्पष्ट विजेता: Numpy

यह भी ध्यान दें कि डेटा संरचना बनाने में योग के रूप में ज्यादा समय लगता है, यदि अधिक नहीं। मेमोरी का आवंटन धीमा है।

उन का मेमोरी उपयोग:

sys.getsizeof(L)
90000112
sys.getsizeof(A)
81940352
sys.getsizeof(N)
80000096

इसलिए ये अलग-अलग ओवरहेड के साथ प्रति संख्या 8 बाइट लेते हैं। जिस रेंज के लिए हम 32 बिट का उपयोग करते हैं वह पर्याप्त है, इसलिए हम कुछ मेमोरी को सुरक्षित कर सकते हैं।

N=numpy.arange(10**7, dtype=numpy.int32)

sys.getsizeof(N)
40000096

%timeit N.sum()
100 loops, best of 3: 8.35 ms per loop

लेकिन यह पता चला है कि मेरी मशीन पर 64 बिट इनट्स को जोड़ना 32 बिट्स की तुलना में तेज है, इसलिए यह केवल इसके लायक है यदि आप मेमोरी / बैंडविड्थ द्वारा सीमित हैं।








python-internals