সূর্য বলছে, Python cProfile দিয়ে বাধা সৃষ্টিকারী ফাংশনগুলি চিহ্নিত করুন!
আমরা!
উ!
জড়ো হও!
মিয়াও!
নমস্কার, আমি অযোগ্য।
সম্প্রতি, আমি যখন পিসি নিয়ে কাজ করি, তখন রেডিওর মতো করে প্রাক্তন হাল ল্যাবরেটরির মাসাহিরো সাকুরাই-এর চ্যানেলটি ব্যাকগ্রাউন্ডে বাজিয়ে খুব আনন্দ পাই।
আসলে, আমার 3DS নিয়ে খেলার স্মৃতিও ফিরে আসে, এবং সেই সময়ে ইওয়াতা-সান-এর নিন্টেন্ডো ডাইরেক্টগুলি সবসময় মজাদার ছিল। আবার যখন দেখি, তখন ফুজিত্সুর তোশিও ইকেদা-র মতো ব্যক্তিদের ডকুমেন্টারি দেখলে তাদের মধ্যে এক অসাধারণ উদ্দীপনা দেখা যায়, এবং সেই সময়ে বারবার আমেরিকায় যাওয়াটা অবিশ্বাস্য মনে হয়। সেই সময়ে ভিয়েতনাম যুদ্ধও চলছিল, এবং দ্বিতীয় বিশ্বযুদ্ধের পরবর্তী ছায়া থাকা সত্ত্বেও, সেই শক্তি কোথা থেকে এসেছিল?
ভূমিকা
আমি স্ক্রিপ্ট ভাষার এক্সিকিউশন গতি সম্পর্কে খুব বেশি সচেতন ছিলাম না, কিন্তু যখন আমি আমার তৈরি cuckooget চালাচ্ছিলাম, তখন এটি আমার নজরে আসে।
কিছু অংশ Rust-এ লেখা কোড PyO3/maturin ব্যবহার করে ইন্টারপ্রেটারের মধ্যে Rust কোড চালাতে পারে।
সহজভাবে বলতে গেলে, Rust-এ লেখা ফাংশনগুলি Python লাইব্রেরিতে পরিণত হয় এবং Rust-এ লেখা কোডের কম্পাইল করা বাইনারি ফাইলের মাধ্যমে এক্সিকিউট হয়। CPU গণনা সংক্রান্ত প্রক্রিয়ার জন্য, এই বাইনারি ফাইল ব্যবহার করে চালানো দ্রুততর হবে - এই সহজ কারণেই এটি করা হয়। ফলাফলস্বরূপ এটি দ্রুততর হয়েছিল, তবে এতে একটি বড় ভুল ছিল।
প্রথমত, আমি শুধুমাত্র Python কোড দিয়ে কোনো বেঞ্চমার্ক পরীক্ষা না করেই পরিমাপ করেছিলাম।
Go-এর জনক নামে পরিচিত রব পাইক-এর লেখা 'C প্রোগ্রামিং সম্পর্কিত নোটস'-এও বলা হয়েছে:
নিয়ম 1: আপনি জানতে পারবেন না যে একটি প্রোগ্রাম কোথায় সময় ব্যয় করবে। বাধাগুলি অপ্রত্যাশিত জায়গায় ঘটে। অতএব, যতক্ষণ না আপনি নিশ্চিত হন যে বাধাটি কোথায়, ততক্ষণ অনুমান করবেন না বা গতি বাড়ানোর চেষ্টা করবেন না।
যদি তাই হয়, তাহলে পরিমাপ করা উচিত। যদি নিম্ন স্তরের C ভাষার নীতিগুলি এমন হয়, তবে এর উপরের স্তরের ক্ষেত্রে এটি প্রযোজ্য হবে না এমনটা অসম্ভব।
মাঝে মাঝে আমি এই ধরনের জিনিস ভুলে যাই, তাই আমি মনে করি যে 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 নামক একটি বাইনারি ফাইলে সংরক্ষিত হয়।
ফাংশন এক্সিকিউশনের ফলাফল পরীক্ষা করুন
আগের সাইটে Python এক্সিকিউটেবল ফাইল ব্যবহার করা হয়েছিল, কিন্তু আমি আরও সহজে 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-এর মধ্যে or দ্বারা মূল্যায়ন করা হয়েছে এমন মাত্র 7টি স্থান আছে, যার মধ্যে 3টি এখানে ব্যবহৃত হয়েছে।
কিছু একটা এখানে সন্দেহজনক মনে হচ্ছে।
সুতরাং, যেহেতু আলোচনা bs4 নিয়ে শুরু হয়েছে, তাই এখানেই শেষ করছি।
আবার দেখা হবে।