ऑप्टिमाइज़ करने से पहले मापें: timeit और cProfile, GIL असल में क्या रोकता है, जनरेटर की मितव्ययिता और स्ट्रिंग तथा स्लाइस के मेमोरी जाल।
इस पाठ को Kodokon में खोलेंकभी अनुमान न लगाएँ, मापें। timeit एक भरोसेमंद माइक्रो-बेंचमार्क अलग कर देता है: यह गार्बेज कलेक्टर को बंद कर देता है और शोर को समतल करने के लिए निष्पादन दोहराता है। cProfile पूरे प्रोग्राम की कॉल का नक्शा बनाता है - असली दोषियों की पहचान के लिए cumtime (संचयी समय, उप-कॉल सहित) से क्रमबद्ध करें। किसी भी प्रदर्शन कार्य का स्वर्णिम नियम: धीमी लाइन लगभग कभी वह नहीं होती जिस पर आपको शक है।
python -m timeit "sum(x * x for x in range(1000))"
python -m timeit -s "s = 'abc' * 100" "s.upper()"
python -m cProfile -s cumtime -m timeit "min(range(50))"GIL (ग्लोबल इंटरप्रेटर लॉक) गारंटी देता है कि किसी प्रक्रिया के भीतर एक समय में केवल एक थ्रेड Python बाइटकोड निष्पादित करे। परिणाम: थ्रेड शुद्ध गणना को तेज़ नहीं करते। फिर भी वे I/O के लिए उत्कृष्ट बने रहते हैं, क्योंकि नेटवर्क या डिस्क पठन के दौरान GIL छोड़ दिया जाता है, और NumPy जैसी कई C लाइब्रेरियों द्वारा भी। गणना से कई कोर संतृप्त करने के लिए, multiprocessing या concurrent.futures.ProcessPoolExecutor से होकर जाएँ। Python 3.13 बिना GIL वाला एक प्रायोगिक "free-threaded" संस्करण देता है, जो प्रोडक्शन में अब भी दुर्लभ है।
import sys
squares_list = [x * x for x in range(1_000_000)]
squares_gen = (x * x for x in range(1_000_000))
print(sys.getsizeof(squares_list), "bytes")
print(sys.getsizeof(squares_gen), "bytes")
print(sum(squares_gen))कोई जनरेटर अनुक्रम की लंबाई चाहे जो हो, कुछ सौ बाइट का ही होता है: यह केवल अपनी निष्पादन स्थिति संग्रहीत करता है और हर वैल्यू माँग पर उत्पन्न करता है। उन्हें पाइपलाइन में जोड़ें - sum(x * x for x in data) - ताकि बिना कोई मध्यवर्ती लिस्ट बनाए धाराओं पर काम हो सके। एक समझौता जिसे जानना ज़रूरी है: कोई जनरेटर एक ही बार इस्तेमाल होता है। एक बार समाप्त हो जाने पर, वह चुपचाप कुछ और उत्पन्न नहीं करता - यह उन बग का क्लासिक स्रोत है जहाँ दूसरा इटरेशन "खाली" लगता है।
import timeit
def concat(n: int) -> str:
out = ""
for i in range(n):
out += str(i)
return out
def join(n: int) -> str:
return "".join(str(i) for i in range(n))
t1 = timeit.timeit(lambda: concat(20_000), number=10)
t2 = timeit.timeit(lambda: join(20_000), number=10)
print(f"concat: {t1:.3f} s / join: {t2:.3f} s")sys.getsizeof इतना छोटा क्यों रहता है?out += part को ''.join(parts) क्यों मात देता है?