सूर्य कहता है, पायथन cProfile के साथ बॉटलनेक फ़ंक्शन की पहचान करें!
हम!
ऊ!
करीब आओ!
म्याऊ!
नमस्ते, मैं अक्षम हूँ।
हाल ही में, मुझे मासाहिरो सकुराई के चैनल को बैकग्राउंड में रेडियो की तरह सुनना बहुत पसंद है, जो पहले हाल लेबोरेटरीज़ से थे, जब मैं अपने पीसी पर काम कर रहा होता हूँ।
वास्तव में, मुझे 3DS के साथ खेलने की यादें भी ताज़ा हो गईं, और उस समय इवाटा-शचो के निंटेंडो डायरेक्ट हमेशा मज़ेदार होते थे। जब मैं उन्हें फिर से देखता हूँ, तो मुझे फुजित्सु के तोशियो इकेडा जैसे अन्य लोग याद आते हैं; वृत्तचित्रों को देखने पर उनमें बहुत ऊर्जा दिखती है, और उस समय अमेरिका की कई यात्राएँ करना एक अविश्वसनीय दुनिया थी। उस समय वियतनाम युद्ध भी चल रहा था, और द्वितीय विश्व युद्ध के बाद युद्ध के बाद की छाया भी थी, फिर भी वह सारी ऊर्जा कहाँ से आई?
परिचय
मैं स्क्रिप्टिंग भाषाओं की निष्पादन गति के बारे में ज्यादा जागरूक नहीं था, लेकिन जब मैं अपना बनाया हुआ cuckooget चला रहा था, तो मुझे इसके बारे में सोचना पड़ा।
कुछ हिस्सों में, PyO3/maturin का उपयोग करके इंटरप्रेटर के भीतर रस्ट में लिखे कोड को निष्पादित किया जा सकता है।
सरल शब्दों में, रस्ट में लिखे फ़ंक्शन को पायथन लाइब्रेरी में बदल दिया जाता है, और निष्पादन रस्ट में लिखे कोड की संकलित बाइनरी फ़ाइल के माध्यम से होता है। इसका सीधा सा कारण यह है कि सीपीयू गणना जैसे कार्यों के लिए इस बाइनरी फ़ाइल को निष्पादित करना तेज़ होगा। परिणामस्वरूप, यह तेज़ हो गया, लेकिन इसमें एक बड़ी गलती है।
सबसे पहले, मैंने केवल पायथन कोड के साथ बेंचमार्क परीक्षण किए बिना ही माप किया था।
जैसा कि गो के जनक कहे जाने वाले रॉब पाइक ने 'सी प्रोग्रामिंग पर नोट्स' में लिखा है:
नियम 1: आप नहीं जान सकते कि आपका प्रोग्राम कहाँ समय खर्च करेगा। बॉटलनेक आश्चर्यजनक स्थानों पर होते हैं। इसलिए, जब तक आप यह स्पष्ट न कर लें कि बॉटलनेक कहाँ है, तब तक अनुमान न लगाएं या स्पीड हैक न करें।
यदि ऐसा है, तो माप किया जाना चाहिए। यदि निम्न-स्तरीय सी भाषा के सिद्धांत ऐसे हैं, तो यह असंभव है कि वे उच्च-स्तरीय परतों पर लागू न हों।
मैं कभी-कभी ऐसी बातें भूल जाता हूँ, इसलिए मुझे लगता है कि UNIX दर्शन जैसी चीज़ों की नियमित रूप से जाँच करना आत्म-अनुशासन के लिए आवश्यक है।
मैंने यह लिखा है, लेकिन एक इंसान होने के नाते, मैं जानता हूँ कि मैं अक्सर अपवादों को आकर्षित करता हूँ...
cProfile के साथ मापन
शुरुआत में, मैं import time से शुरू करके, start_time = time.time() और फ़ंक्शन के निष्पादन के अंत में end_time = time.time() के बाद print(f"exec time {start_time - end_time}") जैसे आदिम तरीकों का उपयोग कर रहा था, लेकिन यह कितना अक्षम है जब हम कंप्यूटर का उपयोग कर रहे हैं! इसलिए, मैंने कुछ ऐसा खोजने की कोशिश की जो प्रत्येक फ़ंक्शन के लिए निष्पादन समय प्रदर्शित कर सके।
ऐसा लगता है कि एक मानक लाइब्रेरी cProfile है।
निम्नलिखित साइट समझने में आसान है:
Python cProfile के साथ कोड प्रदर्शन का विश्लेषण करें
मेरे मामले में, मैंने इसे इस प्रकार सत्यापित किया:
python3 -m cProfile -o profile.pstats ./main.py https://soulminingrig.com/ site
इसके द्वारा, ./main.py द्वारा निष्पादित फ़ंक्शन का निष्पादन समय profile.pstats नामक बाइनरी फ़ाइल में संग्रहीत किया जाता है।
फ़ंक्शन निष्पादन परिणामों की जाँच करें
पिछली साइट पर, इसे पायथन निष्पादन योग्य फ़ाइल का उपयोग करके किया गया था, लेकिन मैं इसे अधिक आसानी से pstats के कमांड-लाइन मोड में प्रवेश करके जाँचूँगा।
python3 -m pstats profile.pstats
यह pstats के कमांड-लाइन मोड में प्रवेश करेगा, और आप नीचे दिए गए आउटपुट परिणामों की जाँच कर सकते हैं।
profile.pstats% stats 5
Thu Nov 7 20:24:10 2024 profile.pstats
48488696 function calls (47625490 primitive calls) in 51.151 seconds
Random listing order was used
List reduced from 3017 to 5 due to restriction <5>
ncalls tottime percall cumtime percall filename:lineno(function)
1586 0.001 0.000 0.001 0.000 /home/haturatu/.local/lib/python3.12/site-packages/aiohttp/client_reqrep.py:909(__del__)
80 0.000 0.000 0.001 0.000 /usr/lib/python3.12/copy.py:247(_reconstruct)
4 0.000 0.000 0.005 0.001 /usr/lib/python3.12/encodings/__init__.py:71(search_function)
3 0.000 0.000 98.745 32.915 /usr/lib/python3.12/asyncio/base_events.py:651(run_until_complete)
1103 0.001 0.000 0.003 0.000 /usr/lib/python3.12/re/_parser.py:312(_class_escape)
आप help के साथ अन्य कमांड की जाँच कर सकते हैं।
profile.pstats% help
Documented commands (type help <topic>):
========================================
EOF add callees callers help quit read reverse sort stats strip
मेरे मामले में, मैंने stats 100 के रूप में आउटपुट किया, इसे एक अलग खुले टर्मिनल से vim में बेतरतीब ढंग से खोला, कॉपी-पेस्ट किया और quit किया।
इस vim में पेस्ट की गई और सहेजी गई टेक्स्ट फ़ाइल के लिए, मैं awk और sort का उपयोग करके प्रत्येक को देखूँगा।
tottime के मामले में
$ awk '{ print $2","$6 }' bench | sort | column -t -s "," | tail
0.009 /usr/lib/python3.12/re/_compiler.py:243(_optimize_charset)
0.010 /usr/lib/python3.12/concurrent/futures/_base.py:428(result)
0.012 /usr/lib/python3.12/asyncio/base_events.py:627(run_forever)
0.013 /usr/lib/python3.12/re/__init__.py:280(_compile)
0.018 /usr/lib/python3.12/re/_compiler.py:37(_compile)
0.022 /usr/lib/python3.12/concurrent/futures/_base.py:497(set_running_or_notify_cancel)
0.030 /home/haturatu/git/devgit/async_web_mirror.py:186(download_and_save_image)
0.031 /usr/lib/python3.12/re/_parser.py:512(_parse)
0.061 /usr/lib/python3.12/concurrent/futures/_base.py:537(set_result)
tottime filename:lineno(function)
किसी तरह, ऐसा लगता है कि tottime के साथ प्रत्येक फ़ंक्शन के लिए बेंचमार्क जैसी जानकारी फिलहाल पर्याप्त है।
क्या ब्यूटीफुल सूप4 का बॉटलनेक सिर्फ एक एहसास है?
अन्य वातावरणों से, ऐसा लग रहा था कि bs4 से कॉल किया गया element.py में search_tag धीमा कर रहा था।
मुझे यहाँ दिलचस्पी थी:
if ((not self.name)
or call_function_with_tag_data
or (markup and self._matches(markup, self.name))
or (not markup and self._matches(markup_name, self.name))):
यह विश्वास करना मुश्किल है कि or ऑपरेशन एक बॉटलनेक है...
हालांकि, इस element.py में केवल 7 स्थान हैं जहाँ or का मूल्यांकन किया जाता है, और उनमें से 3 का उपयोग यहाँ किया जा रहा है।
यह जगह थोड़ी संदिग्ध लगती है।
तो, चूंकि विषय bs4 के बारे में हो गया है, यहीं तक।
फिर मिलेंगे।