profiling.sampling --- پروفایل‌گیر آماری

اضافه شده در نسخه‌ی 3.15.

کد منبع: Lib/profiling/sampling/


نشان‌واره تاکیون

ماژول profiling.sampling که Tachyon نام دارد، پروفایل‌گیری آماری برنامه‌های پایتون را از طریق نمونه‌برداری دوره‌ای پشته فراهم می‌کند. تاکیون می‌تواند اسکریپت‌ها را مستقیماً اجرا کند یا بدون نیاز به تغییرات کد یا راه‌اندازی مجدد، به هر فرایند در حال اجرای پایتون متصل شود. از آنجا که نمونه‌برداری خارج از فرایند هدف انجام می‌شود، سربار عملاً صفر است و این امر تاکیون را هم برای محیط‌های توسعه و هم برای محیط‌های تولید مناسب می‌سازد.

پروفایل‌گیری آماری چیست؟

پروفایل‌گیری آماری با گرفتن تصویرهای لحظه‌ای دوره‌ای از پشته‌ی فراخوانی، تصویری از رفتار برنامه می‌سازد. تاکیون به‌جای ابزارگذاری هر فراخوانی تابع و بازگشت مانند کاری که پروفایل‌گیرهای قطعی انجام می‌دهند، پشته‌ی فراخوانی را در بازه‌های زمانی منظم می‌خواند تا ثبت کند در حال حاضر چه کدی در حال اجراست.

این رویکرد بر یک اصل ساده استوار است: توابعی که زمان قابل‌توجهی از پردازنده را مصرف می‌کنند، به دفعات در نمونه‌های جمع‌آوری‌شده ظاهر خواهند شد. با گردآوری هزاران نمونه در طول یک نشست پروفایل‌گیری، تاکیون تخمین آماری دقیقی از محل صرف زمان می‌سازد. هرچه نمونه‌های بیشتری جمع‌آوری شوند، این تخمین دقیق‌تر می‌شود.

تصویرسازی تعاملی زیر نحوه عملکرد پروفایل‌گیری نمونه‌برداری را نشان می‌دهد. برای تماشای اجرای یک برنامه‌ی پایتون روی پخش (Play) کلیک کنید و مشاهده کنید که چگونه پروفایل‌گیر به‌صورت دوره‌ای تصویرهای لحظه‌ای از پشته‌ی فراخوانی را می‌گیرد. بازه نمونه‌برداری (sample interval) را تنظیم کنید تا ببینید فرکانس نمونه‌برداری چگونه بر نتایج تأثیر می‌گذارد.

نحوه‌ی تخمین زمان

مقادیر زمانی نشان داده شده در خروجی تاکیون تخمین‌هایی مشتق‌شده از تعداد نمونه‌ها هستند، نه اندازه‌گیری‌های مستقیم. تاکیون می‌شمارد که هر تابع چند بار در نمونه‌های جمع‌آوری‌شده ظاهر می‌شود، سپس برای تخمین زمان، آن را در بازه‌ی نمونه‌برداری ضرب می‌کند.

برای مثال، با نرخ نمونه‌برداری ۱۰ کیلوهرتز در یک پروفایل ۱۰ ثانیه‌ای، تاکیون (Tachyon) تقریباً ۱۰۰٬۰۰۰ نمونه جمع‌آوری می‌کند. اگر تابعی در ۵٬۰۰۰ نمونه (۵ درصد کل) ظاهر شود، تاکیون تخمین می‌زند که این تابع ۵ درصد از مدت زمان ۱۰ ثانیه‌ای، یعنی حدود ۵۰۰ میلی‌ثانیه را مصرف کرده است. این یک تخمین آماری است، نه یک اندازه‌گیری دقیق.

دقت این تخمین‌ها به تعداد نمونه بستگی دارد. با ۱۰۰٬۰۰۰ نمونه، تابعی که ۵ درصد را نشان می‌دهد، حاشیه خطای تقریباً ۰.۵± درصد دارد. با تنها ۱٬۰۰۰ نمونه، همان اندازه‌گیری ۵ درصدی در واقع می‌تواند نشان‌دهنده‌ی هر مقداری بین ۳ تا ۷ درصد از زمان واقعی باشد.

به همین دلیل است که مدت زمان‌های طولانی‌ترِ پروفایل‌گیری و بازه‌های نمونه‌برداری کوتاه‌تر نتایج قابل‌اعتماد تری تولید می‌کنند---زیرا نمونه‌های بیشتری جمع‌آوری می‌کنند. برای بیشتر تحلیل‌های عملکرد، تنظیمات پیش‌فرض دقت کافی را برای شناسایی گلوگاه‌ها و هدایت تلاش‌های بهینه‌سازی فراهم می‌کنند.

از آنجا که نمونه‌برداری آماری است، نتایج بین اجراها کمی متفاوت خواهد بود. تابعی که در یک اجرا ۱۲ درصد را نشان می‌دهد ممکن است در اجرای بعدی ۱۱ یا ۱۳ درصد را نشان دهد. این موضوع طبیعی و قابل‌انتظار است. به‌جای درصدهای دقیق، روی الگوی کلی تمرکز کنید و نگران تغییرات کوچک بین اجراها نباشید.

چه زمانی از رویکرد دیگری استفاده کنید

نمونه‌برداری آماری برای هر وضعیتی ایده‌آل نیست.

برای اسکریپت‌های بسیار کوتاهی که در کمتر از یک ثانیه کامل می‌شوند، ممکن است پروفایل‌گیر نمونه‌های کافی برای نتایج قابل‌اعتماد جمع‌آوری نکند. به‌جای آن از profiling.tracing استفاده کنید یا اسکریپت را در یک حلقه اجرا کنید تا زمان پروفایل‌گیری افزایش یابد.

هنگامی که به تعداد فراخوانی‌های دقیق نیاز دارید، نمونه‌برداری نمی‌تواند آن‌ها را فراهم کند. نمونه‌برداری فرکانس را از روی تصویر‌های لحظه‌ای تخمین می‌زند، بنابراین اگر نیاز دارید دقیقاً بدانید یک تابع چند بار فراخوانی شده است، از profiling.tracing استفاده کنید.

هنگام مقایسه‌ی دو پیاده‌سازی که ممکن است تفاوت آن‌ها تنها ۱ تا ۲ درصد باشد، نویز نمونه‌برداری می‌تواند تفاوت‌های واقعی را مبهم کند. برای بنچمارک‌های کوچک از timeit یا برای اندازه‌گیری‌های دقیق از profiling.tracing استفاده کنید.

تفاوت کلیدی با profiling.tracing در نحوه‌ی انجام اندازه‌گیری است. یک پروفایل‌گیر ردیابی (tracing)، کد شما را ابزاربندی می‌کند و هر فراخوانی و بازگشت تابع را ثبت می‌کند. این کار تعداد فراخوانی دقیق و زمان‌بندی دقیقی فراهم می‌کند، اما سرباری به هر فراخوانی تابع اضافه می‌کند. در مقابل، یک پروفایل‌گیر نمونه‌برداری، برنامه را از بیرون در بازه‌های زمانی ثابت بدون تغییر اجرای آن مشاهده می‌کند. تفاوت را این‌گونه تصور کنید: ردیابی مانند این است که فردی شما را دنبال کند و تک‌تک قدم‌هایی که برمی‌دارید را یادداشت کند، در حالی که نمونه‌برداری مانند این است که هر ثانیه عکس بگیرید و مسیر خود را از آن تصاویر لحظه‌ای استنباط کنید.

این مدل مشاهده‌ی خارجی است که پروفایل‌گیری نمونه‌برداری را برای استفاده در محیط عملیاتی (production) کاربردی می‌کند. برنامه‌ی پروفایل‌شده با سرعت کامل اجرا می‌شود زیرا هیچ کد ابزاربندی در داخل آن اجرا نمی‌شود و فرایند هدف هرگز در طول نمونه‌برداری متوقف یا مکث نمی‌شود---تاکیون پشته‌ی فراخوانی را مستقیماً از حافظه‌ی فرایند در حالی که به اجرای خود ادامه می‌دهد می‌خواند. شما می‌توانید به یک سرور در حال اجرا متصل شوید، داده‌ها جمع‌آوری کنید و بدون اینکه برنامه حتی متوجه شود که مشاهده شده است، قطع ارتباط کنید. نکته‌ی منفی این است که توابع بسیار کوتاه ممکن است اگر اتفاقاً بین نمونه‌ها کامل شوند، از دست بروند.

پروفایل‌گیری آماری در پاسخ دادن به این سوال که «برنامه‌ی من زمان خود را کجا صرف می‌کند؟» برتری دارد. این روش نقاط داغ و گلوگاه‌ها را در کد محیط عملیاتی که سرباریِ پروفایل‌گیری قطعی (deterministic) در آن‌ها غیرقابل‌قبول است، آشکار می‌کند. برای تعداد فراخوانی دقیق و گراف‌های فراخوانی کامل، به‌جای آن از profiling.tracing استفاده کنید.

مثال‌های سریع

یک اسکریپت را پروفایل کرده و نتایج را بلافاصله مشاهده کنید:

python -m profiling.sampling run script.py

یک ماژول را با آرگومان‌ها پروفایل کنید:

python -m profiling.sampling run -m mypackage.module arg1 arg2

یک گراف شعله‌ای تعاملی تولید کنید:

python -m profiling.sampling run --flamegraph -o profile.html script.py

با استفاده از PID به یک فرایند در حال اجرا متصل شوید:

python -m profiling.sampling attach 12345

چاپ یک تصویر لحظه‌ای منفرد از پشته‌ی یک فرایند در حال اجرا:

python -m profiling.sampling dump 12345

برای پایش بلادرنگ از حالت زنده استفاده کنید (برای خروج q را فشار دهید):

python -m profiling.sampling run --live script.py

پروفایل‌گیری را به مدت ۶۰ ثانیه با نرخ نمونه‌برداری سریع‌تر انجام دهید:

python -m profiling.sampling run -d 60 -r 20khz script.py

تولید نمودار حرارتی سطربه‌سطر:

python -m profiling.sampling run --heatmap script.py

برای مشاهده‌ی اینکه کدام دستورات بایت‌کد در حال اجرا هستند، پروفایل‌گیری در سطح آپ‌کد را فعال کنید:

python -m profiling.sampling run --opcodes --flamegraph script.py

دستورات

تاکیون (Tachyon) از طریق چندین زیردستور کار می‌کند. run و attach نمونه‌ها را در طول زمان جمع‌آوری می‌کنند؛ dump یک تصویر لحظه‌ای منفرد را ثبت می‌کند؛ replay پروفایل‌های دودویی را به قالب‌های دیگر تبدیل می‌کند.

دستور run

دستور run یک اسکریپت یا ماژول پایتون را اجرا کرده و از زمان راه‌اندازی، آن را پروفایل‌گیری می‌کند:

python -m profiling.sampling run script.py
python -m profiling.sampling run -m mypackage.module

هنگام پروفایل‌گیری یک اسکریپت، پروفایل‌گیر هدف را در یک زیرفرایند راه‌اندازی می‌کند، منتظر مقداردهی اولیه آن می‌ماند و سپس جمع‌آوری نمونه‌ها را آغاز می‌کند. پرچم -m نشان می‌دهد که هدف باید به عنوان یک ماژول اجرا شود (معادل python -m). آرگومان‌های پس از هدف به برنامه‌ی تحت پروفایل‌گیری منتقل می‌شوند:

python -m profiling.sampling run script.py --config settings.yaml

دستور attach

دستور attach با استفاده از شناسه‌ی فرایند، به یک فرایند پایتون که از قبل در حال اجراست متصل می‌شود:

python -m profiling.sampling attach 12345

این فرمان به‌ویژه برای بررسی مشکلات عملکرد در سیستم‌های محیط عملیاتی (production) بسیار ارزشمند است. فرایند هدف به هیچ تغییری نیاز ندارد و نیازی به راه‌اندازی مجدد آن نیست. پروفایل‌گیر متصل می‌شود، نمونه‌ها را برای مدت‌زمان مشخص‌شده جمع‌آوری می‌کند، سپس جدا می‌شود و خروجی تولید می‌کند.

python -m profiling.sampling attach --live 12345
python -m profiling.sampling attach --flamegraph -d 30 -o profile.html 12345

در بیشتر سیستم‌ها، اتصال به یک فرایند دیگر نیازمند مجوزهای مناسب است. برای نیازهای مختص هر سکو، نیازمندی‌های سکو را ببینید.

فرمان dump

فرمان dump یک تصویر لحظه‌ای منفرد از پشته‌ی پایتونِ یک فرایند در حال اجرا را چاپ می‌کند و خارج می‌شود، که مشابه یک ردگیری (traceback) است:

python -m profiling.sampling dump 12345

برخلاف attach، فرمان dump حلقه‌ی نمونه‌برداری را اجرا نمی‌کند: پشته را یک بار می‌خواند. این کار برای بررسی فرایندهای معلق یا پاسخ‌گو نبودن، یا برای پاسخ به این پرسش که «این فرایند همین الان چه کاری انجام می‌دهد؟»، مفید است.

خروجی، یک ردگیری (most recent call last) را منعکس می‌کند و هر نخ را با وضعیت فعلی‌اش حاشیه‌نویسی می‌کند (نخ اصلی، دارای GIL، روی پردازنده، در انتظار GIL، دارای استثنا، یا آماده به کار):

Stack dump for PID 12345, thread 140735 (main thread, has GIL, on CPU; most recent call last):
  File "server.py", line 28, in serve
    await handle_request(req)
  File "handler.py", line 91, in handle_request
    result = expensive_call(req)

هنگامی که پرونده‌های منبع هدف قابل خواندن باشند، dump سطر منبع را برای هر قاب چاپ می‌کند و عبارت در حال اجرا را برجسته می‌کند.

مانند attach، فرمان dump برای خواندن حافظه‌ی فرایند هدف به مجوز نیاز دارد. نیازمندی‌های سکو را ببینید.

فرمان dump از گزینه‌های زیر پشتیبانی می‌کند:

-a، --all-threads

برون‌ریزی تمام نخ‌ها در فرایند هدف. بدون این پرچم، فقط نخ اصلی نمایش داده می‌شود.

--native

شامل قاب‌های مصنوعی <native> که گذارها به افزونه‌های C یا سایر کدهای غیرپایتونی را نشان می‌دهند.

--no-gc

پنهان کردن قاب‌های مصنوعی <GC> که زباله‌روبی فعال را نشان می‌دهند.

--opcodes

حاشیه‌نویسی هر قاب با آپ‌کد بایت‌کدی که نخ در حال حاضر در حال اجرای آن است (برای مثال، opcode=CALL_KW). برای بررسی در سطح دستور، از جمله شناسایی تخصیص‌های انتخاب‌شده توسط مفسر تطبیقی، مفید است.

--async-aware

بازسازی پشته‌ها در سراسر مرزهای await. دستور dump گراف وظیفه را پیمایش می‌کند و یک بخش به ازای هر وظیفه صادر می‌کند، به‌طوری که نشانگرهای <task> هم‌روال‌هایی را که منتظر یکدیگر هستند از هم جدا می‌کنند.

--async-mode {running,all}

کنترل می‌کند که وقتی گزینه‌ی --async-aware فعال است، چه وظیفه‌هایی درج شوند. حالت running تنها وظیفه‌ای را نشان می‌دهد که در حال حاضر روی هر نخ در حال اجرا است؛ حالت all (که پیش‌فرض دستور dump است) همچنین شامل وظیفه‌های معلق در حالت انتظار می‌شود. پیش‌فرض این پرچم برای attach برابر با running است؛ و برای dump به طور پیش‌فرض all است زیرا یک تصویر لحظه‌ای منفرد زمانی بیشترین کارایی را دارد که گراف کامل وظایف را نشان دهد.

--blocking

هنگام خواندن پشته‌ی هدف، تمام نخ‌های آن را متوقف می‌کند و پس از آن از سر می‌گیرد. بهای توقف کوتاه هدف، تضمین یک تصویر لحظه‌ای کاملاً سازگار است. بدون آن، dump حافظه را در حالی می‌خواند که هدف به اجرای خود ادامه می‌دهد، که سریع‌تر است اما گاهی اوقات می‌تواند پشته‌ی ناقصی تولید کند.

دستور replay

دستور replay پرونده‌های پروفایل دودویی را به سایر قالب‌های خروجی تبدیل می‌کند:

python -m profiling.sampling replay profile.bin
python -m profiling.sampling replay --flamegraph -o profile.html profile.bin

این دستور زمانی مفید است که داده‌های پروفایل‌گیری را در قالب دودویی ضبط کرده‌اید و می‌خواهید بعداً آن‌ها را تحلیل کنید یا به یک قالب مصورسازی تبدیل کنید. پروفایل‌های دودویی را می‌توان چندین بار بدون پروفایل‌گیری مجدد به قالب‌های مختلف بازپخش کرد.

# Convert binary to pstats (default, prints to stdout)
python -m profiling.sampling replay profile.bin

# Convert binary to flame graph
python -m profiling.sampling replay --flamegraph -o output.html profile.bin

# Convert binary to gecko format for Firefox Profiler
python -m profiling.sampling replay --gecko -o profile.json profile.bin

# Convert binary to heatmap
python -m profiling.sampling replay --heatmap -o my_heatmap profile.bin

پروفایل‌گیری در محیط تولید

پروفایل‌گیر نمونه‌بردار برای استفاده در محیط تولید طراحی شده است. این ابزار هیچ سربار قابل اندازه‌گیریی روی فرایند هدف تحمیل نمی‌کند زیرا به جای ابزارگذاری کد، حافظه را به صورت بیرونی می‌خواند. برنامه‌ی هدف با سرعت کامل به کار خود ادامه می‌دهد و متوجه نمی‌شود که در حال پروفایل‌گیری است.

هنگام پروفایل‌گیری سیستم‌های تولید، این دستورالعمل‌ها را به خاطر بسپارید:

برای دریافت نتایج سریع با مدت‌زمان‌های کوتاه‌تر (۱۰ تا ۳۰ ثانیه) شروع کنید، سپس اگر به دقت آماری بیشتری نیاز دارید آن را افزایش دهید. به طور پیش‌فرض، پروفایل‌گیری تا زمان اتمام فرایند هدف ادامه می‌یابد، که معمولاً برای شناسایی نقاط داغ اصلی کافی است.

در صورت امکان، به جای ترافیک اوج، در طول بارگذاری نمایشی پروفایل‌گیری کنید. پروفایل‌هایی که در حین عملیات عادی جمع‌آوری می‌شوند راحت‌تر از پروفایل‌هایی که در طول جهش‌های غیرمعمول جمع‌آوری شده‌اند، تفسیر می‌شوند.

خودِ پروفایل‌گیر مقداری پردازنده را روی ماشینی که روی آن اجرا می‌شود (نه روی فرایند هدف) مصرف می‌کند. روی همان ماشین، این مقدار معمولاً ناچیز است. هنگام پروفایل‌گیری فرایندهای راه دور، تأخیر شبکه روی هدف تأثیری نمی‌گذارد.

نتایج حاصل از محیط تولید ممکن است به دلیل اندازه‌های مختلف داده، بارگذاری هم‌زمان، یا اثرات حافظه‌ی پنهان با محیط توسعه متفاوت باشد. این امر قابل انتظار است و اغلب دقیقاً همان چیزی است که می‌خواهید ضبط کنید.

نیازمندی‌های سکو

پروفایل‌گیر برای ضبط ردگیری پشته‌ها، حافظه‌ی فرایند هدف را می‌خواند. این کار در بیشتر سیستم‌های عامل نیازمند دسترسی‌های ارتقایافته است.

لینوکس

در لینوکس، پروفایل‌گیر از ptrace یا process_vm_readv برای خواندن حافظه‌ی فرایند هدف استفاده می‌کند. این کار معمولاً به یکی از موارد زیر نیاز دارد:

  • اجرا به عنوان کاربر root

  • داشتن قابلیت CAP_SYS_PTRACE

  • تنظیم محدوده‌ی Yama ptrace: /proc/sys/kernel/yama/ptrace_scope

مقدار پیش‌فرض ptrace_scope یعنی ۱، ptrace را فقط به فرایندهای والد محدود می‌کند. برای اجازه دادن به اتصال به هر فرایندی که متعلق به همان کاربر است، آن را روی 0 تنظیم کنید:

echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

مک‌اوس (macOS)

در مک‌اوس، پروفایل‌گیر از task_for_pid() برای دسترسی به فرایند هدف استفاده می‌کند. این کار به یکی از موارد زیر نیاز دارد:

  • اجرا به عنوان کاربر root

  • داشتن حق دسترسی com.apple.security.cs.debugger توسط پرونده‌ی دودویی پروفایل‌گیر

  • غیرفعال بودن قابلیت محافظت یکپارچگی سیستم (SIP) (توصیه نمی‌شود)

ویندوز

در ویندوز، پروفایل‌گیر برای خواندن حافظه‌ی یک فرایند دیگر به دسترسی‌های مدیریتی یا حق دسترسی SeDebugPrivilege نیاز دارد.

سازگاری نسخه‌ها

پروفایل‌گیر و فرایند هدف باید نسخه‌ی فرعی (minor) یکسانی از پایتون را اجرا کنند (برای نمونه، هر دو پایتون 3.15). اتصال از پایتون 3.14 به یک فرایند پایتون 3.15 پشتیبانی نمی‌شود.

محدودیت‌های اضافی برای نسخه‌های پیش‌عرضه پایتون اعمال می‌شود: اگر پروفایل‌گیر یا فرایند هدف در حال اجرای نسخه‌ی پیش‌عرضه (آلفا، بتا یا نامزد انتشار) باشد، هر دو باید دقیقاً نسخه‌ی یکسانی را اجرا کنند.

در ساخت‌های نخ‌آزاد (free-threaded) پایتون، پروفایل‌گیر نمی‌تواند از یک ساخت نخ‌آزاد به یک ساخت استاندارد متصل شود، یا برعکس.

پیکربندی نمونه‌برداری

پیش از بررسی فرمت‌های خروجی مختلف و گزینه‌های تجسم‌سازی، مهم است که درک کنید چگونه فرایند نمونه‌برداری را پیکربندی کنید. پروفایل‌گیر چندین گزینه ارائه می‌دهد که کنترل می‌کنند نمونه‌ها با چه فرکانسی جمع‌آوری شوند، فرایند پروفایل‌گیری چقدر طول بکشد، کدام نخ‌ها مشاهده شوند و چه زمینه‌ی اضافی در هر نمونه ثبت شود.

پیکربندی پیش‌فرض برای بیشتر موارد استفاده به خوبی کار می‌کند:

گزینه

پیش‌فرض

پیش‌فرض برای --sampling-rate / -r

۱ کیلوهرتز

پیش‌فرض برای --duration / -d

اجرا تا تکمیل

پیش‌فرض برای --all-threads / -a

فقط نخ اصلی

پیش‌فرض برای --native

بدون قاب‌های <native> (زمان کد C به فراخواننده نسبت داده می‌شود)

پیش‌فرض برای --no-gc

قاب‌های <GC> هنگامی که زباله‌روبی فعال است گنجانده می‌شوند

پیش‌فرض برای --mode

حالت زمان دیواری (تمام نمونه‌ها ثبت می‌شوند)

پیش‌فرض برای --realtime-stats

غیرفعال

پیش‌فرض برای --subprocesses

غیرفعال

پیش‌فرض برای --blocking

غیرفعال (نمونه‌برداری غیرمسدودکننده)

نرخ نمونه‌برداری و مدت‌زمان

دو پارامتر اساسی‌ترین نرخ نمونه‌برداری و مدت‌زمان هستند. این دو با هم تعیین می‌کنند که چه تعداد نمونه در طول یک نشست پروفایل‌گیری جمع‌آوری خواهد شد.

گزینه‌ی --sampling-rate (گزینه‌ی -r) مشخص می‌کند که نمونه‌ها با چه بسامدی جمع‌آوری شوند. مقدار پیش‌فرض ۱ کیلوهرتز (۱۰۰۰ نمونه در ثانیه) است:

python -m profiling.sampling run -r 20khz script.py

نرخ‌های بالاتر نمونه‌های بیشتری را ثبت می‌کنند و داده‌های با دقت‌تری را به قیمت مصرف کمی بالاتر CPU توسط پروفایل‌گیر فراهم می‌کنند. نرخ‌های پایین‌تر سربار پروفایل‌گیر را کاهش می‌دهند اما ممکن است توابع عمرکوتاه را از دست بدهند. برای بیشتر برنامه‌ها، نرخ پیش‌فرض تعادل خوبی بین دقت و سربار فراهم می‌کند.

گزینه‌ی --duration (گزینه‌ی -d) مدت‌زمان پروفایل‌گیری را برحسب ثانیه تنظیم می‌کند. به‌طور پیش‌فرض، پروفایل‌گیری تا زمان خروج فرایند هدف یا متوقف شدن آن ادامه می‌یابد:

python -m profiling.sampling run -d 60 script.py

مشخص کردن مدت‌زمان زمانی مفید است که به فرایندهای طولانی‌مدت متصل می‌شوید یا می‌خواهید پروفایل‌گیری را به یک بازه‌ی زمانی خاص محدود کنید. هنگام پروفایل‌گیری یک اسکریپت، رفتار پیش‌فرضِ اجرای کامل معمولاً همان چیزی است که می‌خواهید.

انتخاب نخ

برنامه‌های پایتون اغلب از چندین نخ استفاده می‌کنند، چه به‌طور صریح از طریق ماژول threading یا به‌طور ضمنی از طریق کتابخانه‌هایی که استخرهای نخ را مدیریت می‌کنند.

به‌طور پیش‌فرض، پروفایل‌گیر تنها نخ اصلی را نمونه‌برداری می‌کند. گزینه‌ی --all-threads (گزینه‌ی -a) نمونه‌برداری از تمام نخ‌ها را در فرایند فعال می‌کند:

python -m profiling.sampling run -a script.py

پروفایل‌گیری چندنخی نشان می‌دهد که کار چگونه در میان نخ‌ها توزیع شده است و می‌تواند نخ‌های مسدودشده یا گرسنه را شناسایی کند. نمونه‌های هر نخ در خروجی ترکیب می‌شوند، با این قابلیت که در برخی قالب‌ها امکان فیلتر کردن بر اساس نخ وجود دارد. این گزینه به‌ویژه هنگام بررسی مسائل هم‌روندی یا زمانی که کار در یک استخر نخ توزیع شده است، مفید است.

حالت مسدودکننده

به‌طور پیش‌فرض، Tachyon حافظه‌ی فرایند هدف را بدون متوقف کردن آن می‌خواند. این رویکرد غیرمسدودکننده برای بیشتر سناریوهای پروفایل‌گیری ایده‌آل است زیرا عملاً هیچ سرباری بر برنامه‌ی هدف تحمیل نمی‌کند: برنامه‌ی پروفایل‌شده با سرعت کامل اجرا می‌شود و از اینکه در حال مشاهده است بیگاه است.

با این حال، نمونه‌برداری غیرمسدودکننده می‌تواند گه‌گاه ردگیری پشته‌های ناقص یا ناسازگار را در برنامه‌هایی با تعداد زیادی تولیدگر یا هم‌روال که به‌سرعت بین نقاط yield جابه‌جا می‌شوند، یا در برنامه‌هایی با پشته‌های فراخوانی بسیار سریعی که توابع بین شروع و پایان یک خوانش تک‌پشته وارد و خارج می‌شوند، تولید کند؛ که این امر منجر به پشته‌های بازسازی‌شده‌ای می‌شود که فریم‌ها را از وضعیت‌های اجرایی مختلف ترکیب می‌کنند یا در واقع هرگز وجود نداشته‌اند.

برای این موارد، گزینه‌ی --blocking فرایند هدف را در طول هر نمونه متوقف می‌کند:

python -m profiling.sampling run --blocking script.py
python -m profiling.sampling attach --blocking 12345

هنگامی که حالت مسدودکننده فعال است، پروفایل‌گیر فرایند هدف را معلق می‌کند، پشته‌ی آن را می‌خواند، سپس آن را از سر می‌گیرد. این کار تضمین می‌کند که هر پشته‌ی ثبت‌شده نمایانگر یک تصویر لحظه‌ای واقعی و سازگار از کارهایی است که فرایند در آن لحظه انجام می‌داد. معاوضه‌ی آن این است که فرایند هدف کندتر اجرا می‌شود زیرا به‌طور مکرر مکث می‌کند.

هشدار

از نرخ‌های نمونه‌برداری بسیار بالا (مقادیر کم --interval) با حالت مسدودکننده استفاده نکنید. معلق کردن و از سرگیری یک فرایند زمان می‌برد، و اگر بازه‌ی نمونه‌برداری خیلی کوتاه باشد، هدف زمان بیشتری را صرف متوقف شدن نسبت به اجرا خواهد کرد. برای حالت مسدودکننده، بازه‌های ۱۰۰۰ میکروثانیه (۱ میلی‌ثانیه) یا بالاتر توصیه می‌شوند. بازه‌ی پیش‌فرض ۱۰۰ میکروثانیه‌ای ممکن است باعث کندی محسوسی در برنامه‌ی هدف شود.

فقط زمانی از حالت مسدودکننده استفاده کنید که پشته‌های ناسازگار را در پروفایل‌های خود مشاهده می‌کنید، به‌ویژه با کدهای دارای تولیدگر زیاد یا هم‌روال زیاد. برای بیشتر برنامه‌ها، حالت غیرمسدودکننده‌ی پیش‌فرض نتایج دقیقی با صفر تأثیر بر فرایند هدف فراهم می‌کند.

فریم‌های خاص

پروفایل‌گیر می‌تواند قاب‌های مصنوعی را به پشته‌های ضبط‌شده تزریق کند تا زمینه‌ی اضافی درباره‌ی آنچه مفسر در زمان گرفته شدن هر نمونه در حال انجام آن است فراهم کند. این قاب‌های مصنوعی به تمایز انواعی از اجرا کمک می‌کنند که در غیر این صورت قابل‌مشاهده نبودند.

گزینه‌ی --native قاب‌های <native> را اضافه می‌کند تا نشان دهد چه زمانی پایتون کدهای C (ماژول‌های توسعه‌ای، توابع توکار یا خود مفسر) را فراخوانی کرده است:

python -m profiling.sampling run --native script.py

این قاب‌ها کمک می‌کنند زمان صرف‌شده در کدهای پایتون را در مقایسه با زمان صرف‌شده در کتابخانه‌های بومی تشخیص دهید. بدون این گزینه، اجرای کد بومی به شکل زمان صرف‌شده در تابع پایتونی که فراخوانی را انجام داده است نمایش داده می‌شود. این کار هنگام بهینه‌سازی کدهایی که استفاده‌ی زیادی از افزونه‌های C مانند NumPy یا درایورهای پایگاه داده دارند مفید است.

به‌طور پیش‌فرض، پروفایل‌گیر هنگامی که زباله‌روبی فعال است، قاب‌های <GC> را نیز در بر می‌گیرد. گزینه‌ی --no-gc این قاب‌ها را حذف می‌کند:

python -m profiling.sampling run --no-gc script.py

قاب‌های زباله‌روبی به شناسایی برنامه‌هایی کمک می‌کنند که زباله‌روبی در آن‌ها زمان قابل‌توجهی را مصرف می‌کند؛ این امر ممکن است الگوهای تخصیص حافظه را نشان دهد که ارزش بهینه‌سازی دارند. اگر زمان قابل‌توجهی را در قاب‌های <GC> مشاهده کردید، بررسی نرخ تخصیص شیء یا استفاده از استخر اشیاء (object pooling) را مد نظر قرار دهید.

پروفایل‌گیری آگاه از آپ‌کد

گزینه‌ی --opcodes پروفایل‌گیری در سطح دستور را فعال می‌کند که ثبت می‌کند چه دستورات بایت‌کد پایتونی در هر نمونه در حال اجرا هستند:

python -m profiling.sampling run --opcodes --flamegraph script.py

این ویژگی دیدگاهی درباره‌ی اجرای بایت‌کد پایتون، از جمله بهینه‌سازی‌های تخصصی تطبیقی، فراهم می‌کند. وقتی یک دستور عام مانند LOAD_ATTR در زمان اجرا به نوع کارآمدتری مانند LOAD_ATTR_INSTANCE_VALUE تبدیل می‌شود، پروفایل‌گیر هم نام تخصصی‌شده و هم دستور پایه را نمایش می‌دهد.

اطلاعات آپ‌کد در چندین قالب خروجی ظاهر می‌شوند:

  • نمودارهای شعله‌ای (Flame graphs): نگه داشتن ماوس روی یک قاب، راهنمای ابزاری با تفکیک دستورات بایت‌کد نمایش می‌دهد که نشان می‌دهد کدام آپ‌کدها زمان را در آن تابع مصرف کرده‌اند

  • نقشه‌ی حرارتی (Heatmap): پنل‌های بایت‌کد قابل‌گسترش در هر سطر از کد منبع، تفکیک دستورات را به همراه درصدهای تخصصی‌سازی نشان می‌دهند

  • حالت زنده (Live mode): یک پنل آپ‌کد، آمارهای سطح دستور را برای تابع انتخاب‌شده نمایش می‌دهد که از طریق ناوبری صفحه‌کلید قابل‌دسترسی است

  • قالب گکو (Gecko format): گذارهای آپ‌کد به عنوان نشانگرهای بازه‌ای در خط زمانی پروفایل‌گیر فایرفاکس منتشر می‌شوند

این سطح از جزئیات به‌ویژه برای موارد زیر مفید است:

  • درک تأثیر عملکردی تخصصی‌سازی تطبیقی پایتون

  • شناسایی دستورات داغ بایت‌کد که ممکن است از بهینه‌سازی بهره‌مند شوند

  • تحلیل اثربخشی الگوهای مختلف کد در سطح دستور

  • اشکال‌زدایی مسائل مربوط به عملکرد که در سطح بایت‌کد رخ می‌دهند

گزینه‌ی --opcodes با قالب‌های --live، --flamegraph، --heatmap و --gecko سازگار است. این گزینه برای ذخیره‌ی اطلاعات آپ‌کد به حافظه‌ی اضافی نیاز دارد و ممکن است عملکرد نمونه‌برداری را به میزان ناچیزی کاهش دهد، اما دید بی‌سابقه‌ای نسبت به مدل اجرایی پایتون فراهم می‌کند.

آمار بلادرنگ

گزینه‌ی --realtime-stats آمارهای نرخ نمونه‌برداری را در طول پروفایل‌گیری نمایش می‌دهد:

python -m profiling.sampling run --realtime-stats script.py

این کار نرخ نمونه‌برداری واقعیِ به‌دست‌آمده را نشان می‌دهد که اگر پروفایل‌گیر نتواند همگام شود، ممکن است کمتر از مقدار درخواست‌شده باشد. این آمار کمک می‌کند تا مطمئن شوید پروفایل‌گیری به درستی کار می‌کند و نمونه‌های کافی در حال جمع‌آوری هستند. برای جزئیات بیشتر درباره‌ی تفسیر این معیارهای سنجش، به کارایی نمونه‌برداری مراجعه کنید.

پروفایل‌گیری زیرفرایند

گزینه‌ی --subprocesses پروفایل‌گیری خودکار زیرفرایندهای ایجادشده توسط هدف را فعال می‌کند:

python -m profiling.sampling run --subprocesses script.py
python -m profiling.sampling attach --subprocesses 12345

هنگامی که این قابلیت فعال باشد، پروفایل‌گیر، فرایند هدف را برای ایجاد فرایند فرزند زیر نظر می‌گیرد. با شناسایی یک فرایند فرزند جدید پایتون، یک نمونه‌ی جداگانه‌ی پروفایل‌گیر به‌طور خودکار برای پروفایل‌گیری آن راه‌اندازی می‌شود. این ویژگی برای برنامه‌هایی که از multiprocessing، subprocess، concurrent.futures همراه با ProcessPoolExecutor یا سایر سازوکارهای ایجاد فرایند استفاده می‌کنند، مفید است.

worker_pool.py
from concurrent.futures import ProcessPoolExecutor
import math

def compute_factorial(n):
    total = 0
    for i in range(50):
        total += math.factorial(n)
    return total

if __name__ == "__main__":
    numbers = [5000 + i * 100 for i in range(50)]
    with ProcessPoolExecutor(max_workers=4) as executor:
        results = list(executor.map(compute_factorial, numbers))
    print(f"Computed {len(results)} factorials")
python -m profiling.sampling run --subprocesses --flamegraph worker_pool.py

این کار نمودارهای شعله‌ی (flame graph) جداگانه‌ای برای فرایند اصلی و هر فرایند کارگر تولید می‌کند: flamegraph_<main_pid>.html، flamegraph_<worker1_pid>.html و غیره.

هر زیرفرایند پرونده‌ی خروجی خودش را دریافت می‌کند. نام پرونده از مسیر خروجی مشخص‌شده (یا پیش‌فرض) به همراه شناسه‌ی فرایند زیرفرایند که به آن اضافه شده است، مشتق می‌شود:

  • اگر -o profile.html را مشخص کنید، زیرفرایندها profile_12345.html، profile_12346.html و غیره را تولید می‌کنند

  • با خروجی پیش‌فرض، زیرفرایندها پرونده‌هایی مانند flamegraph_12345.html یا پوشه‌هایی مانند heatmap_12345 تولید می‌کنند

  • برای قالب pstats (که به طور پیش‌فرض روی خروجی استاندارد است)، زیرفرایندها پرونده‌هایی مانند profile_12345.pstats تولید می‌کنند

پروفایل‌گیرهای زیرفرایند بیشتر گزینه‌های نمونه‌برداری را از والد به ارث می‌برند (نرخ نمونه‌برداری، مدت‌زمان، انتخاب نخ، فریم‌های بومی، فریم‌های GC، حالت آگاه از ناهمگامی و قالب خروجی). تمام فرایندهای فرزند پایتون، از جمله نوه‌ها و فرزندان بعدی، به طور بازگشتی پروفایل‌گیری می‌شوند.

شناسایی زیرفرایند با پویش دوره‌ای برای یافتن فرزندان جدید فرایند هدف و بررسی این موضوع انجام می‌شود که آیا هر فرایند جدید با بررسی حافظه‌ی فرایند برای ساختارهای زمان اجرای پایتون، یک فرایند پایتون است یا خیر. زیرفرایندهای غیرپایتونی (مانند دستورات پوسته یا ابزارهای خارجی) نادیده گرفته می‌شوند.

محدودیتی وجود دارد که حداکثر به ۱۰۰ پروفایل‌گیر زیرفرایند هم‌زمان اجازه می‌دهد تا از اتمام منابع در برنامه‌هایی که فرایندهای زیادی ایجاد می‌کنند، جلوگیری شود. اگر به این محدودیت برسید، زیرفرایندهای اضافی پروفایل‌گیری نمی‌شوند و یک هشدار چاپ خواهد شد.

گزینه‌ی --subprocesses با حالت --live سازگار نیست؛ زیرا حالت زنده از یک رابط پایانهی تعاملی استفاده می‌کند که نمی‌تواند چندین نمایشگر هم‌زمان پروفایل‌گیر را در خود جای دهد.

کارایی نمونه‌برداری

معیارهای کارایی نمونه‌برداری به ارزیابی کیفیت داده‌های جمع‌آوری‌شده کمک می‌کنند. این معیارها در خروجی پایانه‌ی پروفایل‌گیر و در نوار کناری نمودار شعله‌ای نمایش داده می‌شوند.

کارایی نمونه‌برداری درصد تلاش‌های نمونه‌برداری موفق است. هر تلاش نمونه‌برداری، پشته‌ی فراخوانی فرایند هدف را از حافظه می‌خواند. اگر فرایند در لحظه‌ی خواندن در وضعیتی نامنسجم باشد، مانند زمان تغییر زمینه یا هنگامی که مفسر در حال به‌روزرسانی ساختارهای داخلی خود است، تلاش ممکن است شکست بخورد. کارایی پایین ممکن است نشان‌دهنده‌ی این باشد که پروفایل‌گیر نتوانسته است با نرخ نمونه‌برداری درخواست‌شده همگام شود، که اغلب به دلیل بارگذاری سیستم یا تنظیم بازه‌ی بیش‌ازحد تهاجمی است.

نمونه‌های ازدست‌رفته درصد نمونه‌های پیش‌بینی‌شده‌ای است که جمع‌آوری نشده‌اند. بر اساس بازه و مدت‌زمان پیکربندی‌شده، پروفایل‌گیر انتظار دارد تعداد مشخصی نمونه جمع‌آوری کند. اگر پروفایل‌گیر از برنامه عقب بیفتد، مثلاً وقتی سیستم تحت بارگذاری سنگینی است، ممکن است برخی از نمونه‌ها از دست بروند. درصد کمی از نمونه‌های ازدست‌رفته طبیعی است و تأثیر چشمگیری بر دقت آماری پروفایل ندارد.

هر دو معیار جنبه‌ی اطلاع‌رسانی دارند. حتی با وجود برخی تلاش‌های ناموفق یا نمونه‌های ازدست‌رفته، تا زمانی که تعداد کافی نمونه جمع‌آوری شده باشد، پروفایل از نظر آماری معتبر باقی می‌ماند. پروفایل‌گیر تعداد واقعی نمونه‌های ضبط‌شده را گزارش می‌کند که می‌توانید از آن برای قضاوت درباره‌ی کافی بودن داده‌ها برای تحلیل خود استفاده کنید.

حالت‌های پروفایل‌گیری

پروفایل‌گیر نمونه‌برداری از چهار حالت پشتیبانی می‌کند که مشخص می‌کنند چه نمونه‌هایی ضبط شوند. این حالت تعیین می‌کند که پروفایل چه چیزی را اندازه‌گیری کند: کل زمان سپری‌شده، زمان اجرای پردازنده، زمان صرف‌شده برای نگه داشتن قفل مفسر سراسری، یا مدیریت استثنا.

حالت ساعت دیواری

حالت ساعت دیواری (--mode=wall) تمام نمونه‌ها را بدون در نظر گرفتن اینکه نخ در حال انجام چه کاری است، ضبط می‌کند. این حالت پیش‌فرض است و تصویر کاملی از محل گذر زمان در طول اجرای برنامه ارائه می‌دهد:

python -m profiling.sampling run --mode=wall script.py

در حالت ساعت دیواری، نمونه‌ها صرف نظر از اینکه نخ به‌طور فعال در حال اجرای کد پایتون، انتظار برای ورودی/خروجی، مسدودشده روی یک قفل، یا در حال توقف باشد، ثبت می‌شوند. این امر پروفایل‌گیری ساعت دیواری را برای درک توزیع کلی زمان در برنامه‌تان، از جمله زمان صرف‌شده در انتظار، ایده‌آل می‌سازد.

اگر برنامه‌ی شما زمان قابل‌توجهی را در عملیات‌های ورودی/خروجی، فراخوانی‌های شبکه یا توقف سپری کند، حالت ساعت دیواری این انتظارات را به عنوان زمانی که به تابع فراخواننده نسبت داده می‌شود نشان خواهد داد. این اغلب دقیقا همان چیزی است که هنگام بهینه‌سازی تاخیر سرتاسری می‌خواهید.

حالت پردازنده

حالت پردازنده (--mode=cpu) نمونه‌ها را تنها زمانی ضبط می‌کند که نخ در واقع روی یک هسته‌ی پردازنده در حال اجرا باشد:

python -m profiling.sampling run --mode=cpu script.py

نمونه‌هایی که در زمان توقف نخ، مسدود بودن روی ورودی/خروجی یا انتظار برای قفل گرفته شده‌اند، کنار گذاشته می‌شوند. پروفایل حاصل نشان می‌دهد که چرخه‌های پردازنده کجا مصرف می‌شوند و زمان بی‌عطلی را فیلتر می‌کند.

حالت پردازنده زمانی مفید است که می‌خواهید بدون حواس‌پرتی توسط انتظارهای ورودی/خروجی، روی نقاط داغ محاسباتی تمرکز کنید. اگر برنامه‌ی شما بین محاسبات و فراخوانی‌های شبکه جابجا شود، حالت پردازنده نشان می‌دهد که کدام بخش‌های محاسباتی پرهزینه‌تر هستند.

مقایسه‌ی پروفایل‌های ساعت دیواری و پردازنده

اجرای پروفایل‌های حالت ساعت دیواری و پردازنده می‌تواند مشخص کند که آیا زمان یک تابع صرف محاسبه می‌شود یا انتظار.

اگر یک تابع در هر دو پروفایل به طور برجسته ظاهر شود، این یعنی با یک نقطه‌ی داغ محاسباتی واقعی روبه‌رو هستید که به طور فعال از پردازنده (CPU) استفاده می‌کند. بهینه‌سازی باید روی بهبودهای الگوریتمی یا کد کارآمدتر متمرکز شود.

اگر یک تابع در حالت زمان دیواری (wall-clock) بالا اما در حالت پردازنده پایین یا غایب باشد، یعنی وابسته به ورودی/خروجی (I/O-bound) است یا در حال انتظار به سر می‌برد. این تابع بیشتر وقت خود را صرف انتظار برای شبکه، دیسک، قفل‌ها یا توقف (sleep) می‌کند. بهینه‌سازی پردازنده در اینجا کمکی نمی‌کند؛ به جای آن، ورودی/خروجی ناهمگام (async I/O)، استخر اتصال (connection pooling) یا کاهش زمان انتظار را در نظر بگیرید.

import time

def do_sleep():
    time.sleep(2)

def do_compute():
    sum(i**2 for i in range(1000000))

if __name__ == "__main__":
    do_sleep()
    do_compute()
python -m profiling.sampling run --mode=wall script.py  # do_sleep ~98%, do_compute ~1%
python -m profiling.sampling run --mode=cpu script.py   # do_sleep absent, do_compute dominates

حالت GIL

حالت GIL (--mode=gil) نمونه‌ها را تنها زمانی ثبت می‌کند که نخ، قفل مفسر سراسری (global interpreter lock) پایتون را در اختیار داشته باشد:

python -m profiling.sampling run --mode=gil script.py

قفل GIL تنها در هنگام اجرای بایت‌کد (bytecode) پایتون نگه داشته می‌شود. هنگامی که پایتون افزونه‌های C را فراخوانی می‌کند، عملیات ورودی/خروجی انجام می‌دهد یا کد بومی را اجرا می‌کند، GIL معمولاً آزاد می‌شود. این یعنی حالت GIL به‌طور مؤثری زمان صرف‌شده به‌طور خاص برای اجرای کد پایتون را اندازه‌گیری می‌کند و زمان مصرف‌شده در کتابخانه‌های بومی را فیلتر می‌کند.

در برنامه‌های چندنخی (multi-threaded)، حالت GIL نشان می‌دهد که کدام کد مانع از اجرای بایت‌کد پایتون توسط نخ‌های دیگر می‌شود. از آنجا که در هر لحظه فقط یک نخ می‌تواند GIL را در اختیار داشته باشد، توابعی که به دفعات در پروفایل‌های حالت GIL ظاهر می‌شوند، مفسر را در انحصار خود درآورده‌اند.

حالت GIL کمک می‌کند تا به سؤالاتی مانند «کدام تابع‌ها GIL را انحصاری کرده‌اند؟» و «چرا نخ‌های دیگر من معطل (starving) مانده‌اند؟» پاسخ دهید. همچنین می‌تواند در برنامه‌های تک‌نخی مفید باشد تا زمان اجرای پایتون را از زمان صرف‌شده در افزونه‌های C یا ورودی/خروجی متمایز کنید.

import hashlib

def hash_work():
    # C extension - releases GIL during computation
    for _ in range(200):
        hashlib.sha256(b"data" * 250000).hexdigest()

def python_work():
    # Pure Python - holds GIL during computation
    for _ in range(3):
        sum(i**2 for i in range(1000000))

if __name__ == "__main__":
    hash_work()
    python_work()
python -m profiling.sampling run --mode=cpu script.py  # hash_work ~42%, python_work ~38%
python -m profiling.sampling run --mode=gil script.py  # hash_work ~5%, python_work ~60%

حالت استثنا

حالت استثنا (--mode=exception) نمونه‌ها را تنها زمانی ثبت می‌کند که یک نخ دارای یک استثنای فعال باشد:

python -m profiling.sampling run --mode=exception script.py

نمونه‌ها در دو موقعیت ثبت می‌شوند: هنگامی که یک استثنا در پشته‌ی فراخوانی به بالا انتشار می‌یابد (پس از raise و پیش از گرفته شدن)، یا هنگامی که کد در داخل یک بلوک except در حال اجراست که در آن اطلاعات استثنا هنوز در وضعیت نخ (thread state) موجود است.

مثال زیر نشان می‌دهد که کدام نواحی کد ثبت می‌شوند:

def example():
    try:
        raise ValueError("error")    # Captured: exception being raised
    except ValueError:
        process_error()              # Captured: inside except block
    finally:
        cleanup()                    # NOT captured: exception already handled

def example_propagating():
    try:
        try:
            raise ValueError("error")
        finally:
            cleanup()                # Captured: exception propagating through
    except ValueError:
        pass

def example_no_exception():
    try:
        do_work()
    finally:
        cleanup()                    # NOT captured: no exception involved

توجه داشته باشید که بلوک‌های finally تنها زمانی ثبت می‌شوند که یک استثنا به طور فعال در حال انتشار از میان آن‌ها باشد. هنگامی که اجرای یک بلوک except به پایان می‌رسد، پایتون پیش از اجرای هر بلوک finally بعدی، اطلاعات استثنا را پاک می‌کند. به همین ترتیب، بلوک‌های finally که در طول اجرای عادی (هنگامی که هیچ استثنایی بالا نیامده است) اجرا می‌شوند، ثبت نمی‌شوند زیرا هیچ وضعیت استثنایی وجود ندارد.

این حالت برای درک این موضوع مفید است که برنامه‌ی شما چه مقدار از زمان خود را صرف پردازش خطاها می‌کند. مدیریت استثنا می‌تواند در کدی که از استثناها برای کنترل جریان استفاده می‌کند (مانند StopIteration در پیمایش‌گرها) یا در برنامه‌هایی که شرایط خطای زیادی را پردازش می‌کنند (مانند سرورهای شبکه‌ای که با خطاهای اتصال سروکار دارند)، منبع قابل‌توجهی از سربار باشد.

حالت استثنا کمک می‌کند به پرسش‌هایی مانند «چه مقدار زمان صرف رسیدگی به استثناها می‌شود؟» و «کدام هندلرهای استثنا پرهزینه‌تر هستند؟» پاسخ دهید. این حالت می‌تواند هزینه‌های عملکردی پنهان را در کدی که تعداد زیادی استثنا را صید و فرایند می‌کند، آشکار سازد؛ حتی زمانی که آن استثناها به شکلی مناسب مدیریت شوند. برای مثال، اگر یک کتابخانه‌ی تجزیه به صورت داخلی از استثناها برای سیگنال دادن خطاهای قالب استفاده کند، این حالت زمان صرف‌شده در آن هندلرها را ضبط خواهد کرد، حتی اگر کد فراخواننده هرگز استثناها را نبیند.

قالب‌های خروجی

پروفایل‌گیر خروجی را در چندین قالب تولید می‌کند که هر یک برای جریان‌کارهای تحلیل مختلفی مناسب هستند. این قالب با یک پرچم خط فرمان انتخاب می‌شود و بسته به قالب، خروجی به stdout، یک پرونده یا یک پوشه هدایت می‌شود.

قالب pstats

قالب pstats (--pstats) یک جدول متنی مشابه با آنچه پروفایل‌گیرهای قطعی تولید می‌کنند، می‌سازد. این قالب خروجی پیش‌فرض است:

python -m profiling.sampling run script.py
python -m profiling.sampling run --pstats script.py
خروجی ترمینال pstats در Tachyon

قالب pstats نتایج پروفایل‌گیری را در یک جدول رنگ‌بندی‌شده نمایش می‌دهد که نقاط داغ تابع، تعداد نمونه‌ها و تخمین‌های زمانی را نشان می‌دهد.

خروجی به طور پیش‌فرض روی stdout ظاهر می‌شود:

Profile Stats (Mode: wall):
     nsamples  sample%    tottime (ms)  cumul%   cumtime (ms)  filename:lineno(function)
       234/892    11.7%       234.00     44.6%       892.00    server.py:145(handle_request)
       156/156     7.8%       156.00      7.8%       156.00    <built-in>:0(socket.recv)
        98/421     4.9%        98.00     21.1%       421.00    parser.py:67(parse_message)

ستون‌ها تعداد نمونه‌برداری و زمان‌های تخمینی را نشان می‌دهند:

  • nsamples: به صورت direct/cumulative (برای مثال 10/50) نمایش داده می‌شود. نمونه‌های مستقیم زمانی هستند که تابع در بالای پشته قرار داشته و به طور فعال در حال اجرا بوده است. نمونه‌های تجمعی زمانی هستند که تابع در هر نقطه‌ای از پشته ظاهر شده است، از جمله زمانی که منتظر توابع فراخوانی‌شده‌ی خود بوده است. اگر یک تابع مقدار 10/50 را نشان دهد، یعنی در ۱۰ نمونه به طور مستقیم در حال اجرا بوده و در مجموع در ۵۰ نمونه روی پشته‌ی فراخوانی حضور داشته است.

  • sample% و cumul%: به ترتیب درصدهای کل نمونه‌ها برای تعداد مستقیم و تجمعی.

  • tottime و cumtime: زمان ساعت دیواری تخمینی بر اساس تعداد نمونه‌ها و مدت زمان پروفایل‌گیری. واحدهای زمانی به طور خودکار بر اساس بزرگی مقدار انتخاب می‌شوند: ثانیه برای مقادیر بزرگ، میلی‌ثانیه برای مقادیر متوسط، یا میکروثانیه برای مقادیر کوچک.

خروجی شامل یک راهنما (Legend) برای توضیح هر ستون و خلاصه‌ای از توابع جالب است که موارد زیر را برجسته می‌کند:

  • نقاط داغ (Hot spots): توابعی با نسبت نمونه‌ی مستقیم به تجمعی بالا (نسبتی نزدیک به ۱.۰). این توابع بیشتر وقت خود را صرف اجرای کد خود می‌کنند تا انتظار برای توابع فراخوانی‌شده. نسبت‌های بالا نشان می‌دهند که زمان CPU در کجا به طور واقعی مصرف می‌شود.

  • فراخوانی‌های غیرمستقیم: توابعی با تفاوت زیاد بین نمونه‌های تجمعی و مستقیم. این‌ها توابع ارکستراسیون هستند که کار را به توابع دیگر محول می‌کنند. آن‌ها به وفور روی پشته ظاهر می‌شوند اما به ندرت در بالا قرار می‌گیرند.

  • بزرگ‌نمایی فراخوانی: توابعی که در آن‌ها نمونه‌های تجمعی به طور قابل‌توجهی از نمونه‌های مستقیم فراتر می‌روند (ضریب تجمعی/مستقیم بالا). این‌ها توابع به طور مکرر تو در تویی هستند که در عمق بسیاری از زنجیره‌های فراخوانی ظاهر می‌شوند.

از --no-summary برای حذف بخش‌های راهنما و خلاصه استفاده کنید.

برای ذخیره‌ی خروجی pstats در یک پرونده دودویی به جای stdout:

python -m profiling.sampling run -o profile.pstats script.py

قالب pstats گزینه‌های متعددی را برای کنترل نحوه نمایش پشتیبانی می‌کند. گزینه‌ی --sort ستون مورد استفاده برای مرتب‌سازی نتایج را مشخص می‌کند:

python -m profiling.sampling run --sort=tottime script.py
python -m profiling.sampling run --sort=cumtime script.py
python -m profiling.sampling run --sort=nsamples script.py

گزینه‌ی --limit خروجی را به N ورودی برتر محدود می‌کند:

python -m profiling.sampling run --limit=30 script.py

گزینه‌ی --no-summary خلاصه سرآیند را که پیش از جدول آمار می‌آید، حذف می‌کند.

قالب پشته‌های فشرده‌شده

قالب پشته‌های فشرده‌شده (--collapsed) برای هر پشته‌ی فراخوانی منحصربه‌فرد یک سطر تولید می‌کند، همراه با تعداد دفعاتی که از آن پشته نمونه‌برداری شده است:

python -m profiling.sampling run --collapsed script.py

خروجی به این صورت است:

main;process_data;parse_json;decode_utf8 42
main;process_data;parse_json 156
main;handle_request;send_response 89

هر سطر شامل نام‌های تابعی است که با نقطه و ویرگول از هم جدا شده‌اند و نمایانگر پشته‌ی فراخوانی از پایین به بالا هستند، و به دنبال آن یک فاصله و تعداد نمونه قرار دارد. این قالب برای سازگاری با ابزارهای نمودار شعله‌ی خارجی، به‌ویژه اسکریپت flamegraph.pl اثر برندان گرگ (Brendan Gregg)، طراحی شده است.

برای تولید یک نمودار شعله از پشته‌های فشرده‌شده:

python -m profiling.sampling run --collapsed script.py > stacks.txt
flamegraph.pl stacks.txt > profile.svg

پرونده‌ی SVG حاصل را می‌توان در هر مرورگر وبی مشاهده کرد و نمایشی تعاملی ارائه می‌دهد که در آن می‌توانید برای بزرگ‌نمایی روی مسیرهای فراخوانی خاص کلیک کنید.

قالب نمودار شعله

قالب نمودار شعله (--flamegraph) یک پرونده‌ی HTML خودمحور با یک نمایشگر نمودار شعله‌ی تعاملی تولید می‌کند:

python -m profiling.sampling run --flamegraph script.py
python -m profiling.sampling run --flamegraph -o profile.html script.py
نمودار شعله‌ی تعاملی Tachyon

نمایش نمودار شعله، پشته‌های فراخوانی را به صورت مستطیل‌های تودرتو با عرض متناسب با زمان صرف‌شده نشان می‌دهد. نوار کناری آمار زمان اجرا، معیارهای GIL و توابع داغ (hotspot) را نمایش می‌دهد.

Try the interactive example!

اگر هیچ پرونده‌ی خروجی‌ای مشخص نشده باشد، پروفایل‌گیر نام پرونده‌ای را بر اساس شناسه‌ی فرایند تولید می‌کند (برای مثال، flamegraph.12345.html).

پرونده‌ی تولیدشده‌ی HTML نیازی به وابستگی‌های خارجی ندارد و می‌توانید آن را مستقیماً در یک مرورگر وب باز کنید. این تصویرسازی پشته‌های فراخوانی را به شکل مستطیل‌های تو در تو نمایش می‌دهد که عرض آن‌ها متناسب با زمان صرف‌شده است. نگه داشتن ماوس روی یک مستطیل جزئیاتی درباره‌ی آن تابع از جمله زمینه‌ی کد منبع را نشان می‌دهد و با کلیک کردن روی آن می‌توانید روی آن بخش از درخت فراخوانی زوم کنید.

رابط نمودار شعله شامل موارد زیر است:

  • یک نوار کناری که خلاصه‌ی پروفایل، آمار نخ‌ها، معیارهای بهره‌وری نمونه‌برداری (نگاه کنید به کارایی نمونه‌برداری) و توابع داغ برتر را نشان می‌دهد

  • قابلیت جستجو که هم از تطبیق نام تابع و هم از الگوهای سطری file.py:42 پشتیبانی می‌کند

  • پالایش به ازای هر نخ از طریق منوی کشویی

  • کلید تغییر تم تاریک/روشن (تنظیمات برگزیده در سراسر نشست‌ها ذخیره می‌شود)

  • خروجی SVG برای ذخیره‌سازی نمای فعلی

بخش آمار نخ‌ها معیارهای رفتار زمان اجرا (ران‌تایم) را نشان می‌دهد:

  • GIL Held: درصدی از نمونه‌ها که در آن‌ها یک نخ قفل مفسر سراسری (global interpreter lock) را در اختیار داشت (به‌طور فعال در حال اجرای کد پایتون بود)

  • GIL Released: درصدی از نمونه‌ها که در آن‌ها هیچ نخی GIL را در اختیار نداشت

  • Waiting GIL: درصدی از نمونه‌ها که در آن‌ها یک نخ در انتظار به‌دست آوردن GIL بود

  • GC: درصدی از نمونه‌ها در طول فرآیند زباله‌روبی

این آمار کمک می‌کند تا رقابت بر سر GIL را شناسایی کنید و بفهمید زمان چگونه بین اجرای پایتون، کد بومی (native) و انتظار توزیع می‌شود.

نمودارهای شعله‌ای به‌ویژه برای شناسایی پشته‌های فراخوانی عمیق و درک ساختار سلسله‌مراتب مصرف زمان مؤثر هستند. مستطیل‌های پهن در بالا توابعی را نشان می‌دهند که زمان قابل‌توجهی را به‌طور مستقیم یا از طریق توابع فراخوانده‌شده‌ی خود مصرف می‌کنند.

نمودارهای شعله‌ای تفاضلی

نمودارهای شعله‌ای تفاضلی دو اجرای پروفایل‌گیری را مقایسه می‌کنند تا مشخص شود کارایی کجا تغییر کرده است. این کار به شناسایی رگرسیون‌های ایجادشده توسط تغییرات کد و تأیید اینکه بهینه‌سازی‌ها به اثر مورد نظر خود رسیده‌اند کمک می‌کند:

# Capture baseline profile
python -m profiling.sampling run --binary -o baseline.bin script.py

# After modifying code, generate differential flamegraph
python -m profiling.sampling run --diff-flamegraph baseline.bin -o diff.html script.py

این تجسم‌سازی، پروفایل فعلی را با عرض قاب‌ها که مصرف زمان فعلی را نشان می‌دهند رسم می‌کند، سپس رنگی را اعمال می‌کند تا نشان دهد هر تابع نسبت به مبنا چگونه تغییر کرده است.

کدگذاری رنگی:

  • قرمز: توابعی که زمان بیشتری مصرف می‌کنند (رگرسیون‌ها). سایه‌های روشن‌تر نشان‌دهنده‌ی افزایش‌های جزئی هستند، در حالی که سایه‌های تیره رگرسیون‌های شدید را نشان می‌دهند.

  • آبی: توابعی که زمان کمتری مصرف می‌کنند (بهبودها). سایه‌های روشن‌تر برای کاهش‌های جزئی و سایه‌های تیره‌تر برای افزایش سرعت‌های قابل‌توجه.

  • خاکستری: تغییر حداقلی یا بدون تغییر.

  • بنفش: توابع جدیدی که در خط مبنا وجود ندارند.

رنگ‌های قاب تغییرات در زمان مستقیم (زمانی که تابع در بالای پشته بوده و به‌طور فعال اجرا می‌شده) را نشان می‌دهند، نه زمان تجمعی شامل توابع فراخوانی‌شده. قرار دادن نشانگر ماوس روی یک قاب، جزئیات مقایسه شامل زمان خط مبنا، زمان فعلی و درصد تغییر را نمایش می‌دهد.

برخی مسیرهای فراخوانی ممکن است به‌طور کامل بین پروفایل‌ها ناپدید شوند. این موارد پشته‌های حذف‌شده (**elided stacks) نامیده می‌شوند و زمانی رخ می‌دهند که بهینه‌سازی‌ها مسیرهای کد را حذف کنند یا برخی شاخه‌ها متوقف شوند. اگر پشته‌های حذف‌شده وجود داشته باشند، یک کلید تغییر وضعیت نمایان می‌شود که به شما اجازه می‌دهد بین نمای تفاضلی اصلی و نمای مختص پشته‌های حذف‌شده (که‌ فقط مسیرهای حذف‌شده را نشان می‌دهد و به رنگ بنفش است) جابه‌جا شوید.

قالب Gecko

قالب Gecko (--gecko) خروجی JSON سازگار با Firefox Profiler تولید می‌کند:

python -m profiling.sampling run --gecko script.py
python -m profiling.sampling run --gecko -o profile.json script.py

ابزار Firefox Profiler یک ابزار مبتنی بر وب پیشرفته است که در اصل برای پروفایل‌گیری از خود فایرفاکس ساخته شده است. این ابزار ویژگی فراتر از نمودارهای شعله‌ی ساده، از جمله نمای زمان‌بندی، کاوش درخت فراخوانی و تجسم نشانگرها را فراهم می‌کند. برای دستورالعمل‌های استفاده‌ی تفصیلی، به مستندات Firefox Profiler مراجعه کنید.

برای استفاده از خروجی، Firefox Profiler را در مرورگر خود باز کرده و پرونده‌ی JSON را بارگذاری کنید. پروفایل‌گیر به‌طور کامل در سمت کلاینت اجرا می‌شود، بنابراین داده‌های پروفایل‌گیری شما هرگز از دستگاه شما خارج نمی‌شوند.

قالب Gecko به‌طور خودکار فراداده‌ی اضافی درباره‌ی وضعیت GIL و فعالیت CPU جمع‌آوری می‌کند و ویژگی‌های تحلیلی مخصوص مدل نخ‌کشی پایتون را فعال می‌سازد. پروفایل‌گیر نشانگرهای بازه‌ای تولید می‌کند که به صورت نوارهای رنگی در زمان‌بندی Firefox Profiler ظاهر می‌شوند:

  • نشانگرهای GIL: نشان می‌دهند که چه زمانی نخ‌ها قفل مفسر سراسری را نگه داشته یا آزاد می‌کنند

  • نشانگرهای CPU: نشان می‌دهند که نخ‌ها چه زمانی روی CPU در حال اجرا هستند در مقایسه با زمانی که بیکارند

  • نشانگرهای نوع کد: کد پایتون را از کد بومی (افزونه‌ی C) متمایز می‌کنند

  • نشانگرهای GC: فعالیت زباله‌روبی را نشان می‌دهند

به همین دلیل، گزینه‌ی --mode با قالب Gecko در دسترس نیست؛ تمام داده‌های مربوط به‌طور خودکار ثبت می‌شوند.

نمای درخت فراخوانی Firefox Profiler

نمای درخت فراخوانی (Call Tree) سلسله‌مراتب کامل فراخوانی را به همراه تعداد نمونه‌ها و درصدها نمایش می‌دهد. نوار کناری آمار تفصیلی تابع انتخاب‌شده شامل زمان اجرا و توزیع نمونه را نمایش می‌دهد.

نمای نمودار شعله‌ی پروفایل‌گیر فایرفاکس

تصویرسازی نمودار شعله، پشته‌های فراخوانی را به شکل مستطیل‌های تو در تو نشان می‌دهد. نام تابع‌ها در سلسله‌مراتب فراخوانی قابل‌مشاهده است.

نمودار نشانگر پروفایل‌گیر فایرفاکس با آپ‌کدها

نمودار نشانگر، نشانگرهای بازه‌ای شامل وضعیت پردازنده، وضعیت GIL و آپ‌کدها را نمایش می‌دهد. با فعال‌سازی --opcodes، دستورالعمل‌های بایت‌کد مانند BINARY_OP_ADD_FLOAT، CALL_PY_EXACT_ARGS و CALL_LIST_APPEND به عنوان نشانگرهایی ظاهر می‌شوند که اجرا را در طول زمان نشان می‌دهند.

قالب نقشه حرارتی

قالب نقشه حرارتی (--heatmap) یک تصویرسازی تعاملی HTML تولید می‌کند که تعداد نمونه‌ها را در سطح سطر منبع نشان می‌دهد:

python -m profiling.sampling run --heatmap script.py
python -m profiling.sampling run --heatmap -o my_heatmap script.py
تصویرسازی نقشه حرارتی Tachyon

نقشه حرارتی، تعداد نمونه‌ها را مستقیماً روی کد منبع شما پوشش می‌دهد. سطرها از سرد (نمونه‌های کم) تا گرم (نمونه‌های زیاد) با رنگ مشخص شده‌اند. دکمه‌های ناوبری (▲▼) به شما اجازه می‌دهند بین فراخواننده‌ها و فراخوانده‌شده‌ها جابه‌جا شوید.

برخلاف سایر قالب‌ها که یک پرونده‌ی واحد تولید می‌کنند، خروجی نقشه حرارتی پوشه‌ای حاوی پرونده‌های HTML برای هر پرونده‌ی منبع پروفایل‌شده ایجاد می‌کند. اگر هیچ مسری برای خروجی مشخص نشود، نام پوشه heatmap_PID خواهد بود.

تصویرسازی نقشه حرارتی، کد منبع شما را با یک طیف رنگی نمایش می‌دهد که نشان می‌دهد چه تعداد نمونه در هر سطر جمع‌آوری شده است. سطرهای گرم (نمونه‌های زیاد) با رنگ‌های گرم و سطرهای سرد (نمونه‌های کم یا بدون نمونه) با رنگ‌های سرد ظاهر می‌شوند. این نما کمک می‌کند دقیقاً مشخص کنید کدام سطرهای کد مسئول مصرف زمان هستند.

رابط نقشه حرارتی چندین ویژگی تعاملی را فراهم می‌کند:

  • حالت‌های رنگ‌آمیزی: جابه‌جایی بین «زمان خودی» (اجرای مستقیم) و «زمان کل» (تجمعی، شامل زمان صرف‌شده در تابع‌های فراخوانده‌شده)

  • پالایش کد سرد: نمایش تمام سطرها یا فقط سطرهای دارای نمونه

  • ناوبری گراف فراخوانی: هر سطر دکمه‌های ناوبری (▲ برای فراخواننده‌ها، ▼ برای فراخوانده‌شده‌ها) را نشان می‌دهد که به شما امکان می‌دهند مسیرهای اجرا را در کد خود ردگیری کنید. هنگامی که چندین تابع از یک سطر فراخوانی شده باشند یا از آن فراخوانی کرده باشند، منویی ظاهر می‌شود که تمام گزینه‌ها را به همراه تعداد نمونه‌هایشان نشان می‌دهد.

  • مینی‌مپ پیمایش: یک نمای کلی عمودی که توزیع حرارت را در کل پرونده نشان می‌دهد

  • فهرست سلسله‌مراتب: پرونده‌ها بر اساس نوع (کتابخانه استاندارد، بسته‌های سایت، پروژه) با تعداد نمونه‌های تجمیعی در هر پوشه سازماندهی شده‌اند

  • تم تاریک/روشن: جابه‌جایی با تنظیماتی که در طول نشست‌ها ذخیره می‌شود

  • پیوند دادن به سطر: برای ایجاد نشانی‌های وب قابل اشتراک‌گذاری، روی شماره‌های سطر کلیک کنید

هنگامی که پروفایل‌گیری در سطح آپ‌کد با --opcodes فعال باشد، می‌توان هر سطر گرم را گسترش داد تا مشخص شود کدام دستورالعمل‌های بایت‌کد زمان مصرف کرده‌اند:

نقشه‌ی حرارتی با تابلوی بایت‌کد گسترش‌یافته

گسترش دادن یک سطر داغ، دستورات بایت‌کد اجراشده، از جمله گونه‌های تخصصی‌شده را آشکار می‌کند. این تابلو تعداد نمونه‌ها به ازای هر دستور و درصد کلی تخصصی‌سازی را برای آن سطر نشان می‌دهد.

نمونه‌ی تعاملی را امتحان کنید!

نقشه‌های حرارتی زمانی بسیار مفید هستند که بدانید کدام پرونده حاوی یک مشکل عملکردی است اما نیاز دارید سطر‌های خاص را شناسایی کنید. بسیاری از توسعه‌دهندگان این قالب را ترجیح می‌دهند زیرا مستقیماً به کد منبع آن‌ها نگاشت می‌شود و خواندن و جابه‌جایی در آن را آسان می‌کند. برای اسکریپت‌های کوچک‌تر و تحلیل‌های متمرکز، نقشه‌های حرارتی نمای بصری شهودی را فراهم می‌کنند که دقیقاً نشان می‌دهد زمان در کجا صرف شده است، بدون اینکه نیازی به تفسیر تجسم‌های سلسله‌مراتبی باشد.

قالب دودویی

قالب دودویی (--binary) یک پرونده دودویی فشرده را برای ذخیره‌سازی کارآمد داده‌های پروفایل‌گیری تولید می‌کند:

python -m profiling.sampling run --binary -o profile.bin script.py
python -m profiling.sampling attach --binary -o profile.bin 12345

گزینه‌ی --compression فشرده‌سازی داده‌ها را کنترل می‌کند:

  • auto (پیش‌فرض): در صورت امکان از فشرده‌سازی zstd استفاده کنید، در غیراین‌صورت هیچ فشرده‌سازی انجام ندهید

  • zstd: فشرده‌سازی zstd را اعمال کنید (نیازمند پشتیبانی compression.zstd است)

  • none: فشرده‌سازی را غیرفعال کنید

python -m profiling.sampling run --binary --compression=zstd -o profile.bin script.py

برای تحلیل پروفایل‌های دودویی، از دستور replay استفاده کنید تا آن‌ها را به قالب‌های دیگری مانند نمودارهای شعله یا خروجی pstats تبدیل کنید.

گردش کار ضبط و پخش مجدد

قالب دودویی در ترکیب با دستور پخش مجدد، یک گردش کار ضبط و پخش مجدد را فراهم می‌کند که ضبط داده‌ها را از تحلیل جدا می‌کند. به‌جای تولید تجسم‌ها در طول پروفایل‌گیری، داده‌های خام را در یک پرونده دودویی فشرده ضبط می‌کنید و بعدها آن‌ها را به قالب‌های مختلف تبدیل می‌کنید.

این رویکرد سه مزیت اصلی دارد:

  • اجرای نمونه‌برداری سریع‌تر انجام می‌شود زیرا کار ساخت ساختارهای داده برای تجسم تا زمان پخش مجدد به تأخیر می‌افتد.

  • یک ضبط دودویی واحد را می‌توان بدون پروفایل‌گیری مجدد به چندین قالب خروجی تبدیل کرد: pstats برای مرور سریع، نمودار شعله برای کاوش بصری، و نقشه‌ی حرارتی برای جزئیات در سطح سطر.

  • پرونده‌های دودویی فشرده هستند و اشتراک‌گذاری آن‌ها با همکارانی که می‌توانند آن‌ها را به قالب دلخواه خود تبدیل کنند، آسان است.

یک گردش کار معمولی:

# Capture profile in production or during tests
python -m profiling.sampling attach --binary -o profile.bin 12345

# Later, analyze with different formats
python -m profiling.sampling replay profile.bin
python -m profiling.sampling replay --flamegraph -o profile.html profile.bin
python -m profiling.sampling replay --heatmap -o heatmap profile.bin

حالت زنده

حالت زنده (--live) یک نمای مبتنی بر پایانه و بلادرنگ از داده‌های پروفایل‌گیری فراهم می‌کند که مشابه دستور top برای فرایندهای سیستم است:

python -m profiling.sampling run --live script.py
python -m profiling.sampling attach --live 12345
حالت زنده‌ی Tachyon که تمام نخ‌ها را نشان می‌دهد

حالت زنده آمار پروفایل‌گیری بلادرنگ را نمایش می‌دهد و داده‌های ترکیبی چندین نخ را در یک برنامه‌ی چندنخی نشان می‌دهد.

با رسیدن نمونه‌های جدید، نمایشگر به‌طور مداوم به‌روزرسانی می‌شود و داغ‌ترین عملکردهای فعلی را نشان می‌دهد. این حالت به ماژول curses نیاز دارد که در سیستم‌های یونیکس‌مانند در دسترس است اما در ویندوز خیر. پایانه باید حداقل ۶۰ ستون عرض و ۱۲ سطر ارتفاع داشته باشد؛ پایانه‌های بزرگ‌تر ستون‌های بیشتری را نمایش می‌دهند.

سرآیند ۳ تابع داغ‌تر اول، معیارهای بهره‌وری نمونه‌برداری و آمارهای وضعیت نخ (درصد نگهداری GIL، میزان استفاده از CPU، زمان GC) را نمایش می‌دهد. جدول اصلی آمار تابع را نشان می‌دهد که ستون در حال حاضر مرتب‌شده با یک پیکان (▼) مشخص شده است.

هنگامی که --opcodes فعال است، یک تابلوی آپ‌کد اضافی در زیر جدول اصلی ظاهر می‌شود که آمار در سطح دستور را برای تابع انتخاب‌شده‌ی فعلی نشان می‌دهد. این تابلو نشان می‌دهد که کدام دستورات بایت‌کد با بسامد بیشتری در حال اجرا هستند، از جمله انواع تخصصی و آپ‌کدهای پایه‌ی آن‌ها.

حالت زنده‌ی Tachyon با تابلوی آپ‌کد

حالت زنده با گزینه‌ی --opcodes فعال، یک تابلوی آپ‌کد همراه با تفکیک دستورات بایت‌کد برای تابع انتخاب‌شده نمایش می‌دهد.

دستورات صفحه‌کلید

در داخل حالت زنده، دستورات صفحه‌کلید نمایش را کنترل می‌کنند:

q

خروج از پروفایل‌گیر و بازگشت به پوسته.

s / S

چرخش بین ترتیب‌های مرتب‌سازی به جلو/عقب (تعداد نمونه، درصد، کل زمان، درصد تجمعی، زمان تجمعی).

p

مکث یا ازسرگیری به‌روزرسانی‌های نمایشگر. نمونه‌برداری در پس‌زمینه در حالی که نمایشگر متوقف است ادامه می‌یابد، بنابراین می‌توانید نما را فریز کنید تا نتایج را بدون متوقف کردن جمع‌آوری داده‌ها بررسی کنید.

r

تمام آمارها را بازنشانی کرده و از نو شروع کنید. این قابلیت پس از اتمام پروفایل‌گیری غیرفعال می‌شود تا از از دست رفتن تصادفی داده‌ها جلوگیری شود.

/

وارد حالت فیلتر شوید تا توابع را بر اساس نام جستجو کنید. این فیلتر برای تطبیق زیررشته‌ها در نام پرونده و نام تابع به بزرگی و کوچکی حروف حساس نیست. یک الگو تایپ کنید و برای اعمال آن کلید Enter و برای انصراف کلید Escape را فشار دهید. الگوهای گلاب (Glob) و عبارت‌های باقاعده پشتیبانی نمی‌شوند.

c

فیلتر فعلی را پاک کرده و دوباره تمام توابع را نمایش دهید.

t

بین مشاهده‌ی تمام نخ‌ها به صورت ترکیبی یا آمار به ازای هر نخ جابه‌جا شوید. در حالت به ازای هر نخ، یک شمارنده‌ی نخ (برای مثال، 1/4) ظاهر می‌شود که موقعیت شما را در میان نخ‌های موجود نشان می‌دهد.

← → or ↑ ↓

در نمای به ازای هر نخ، بین نخ‌ها جابه‌جا شوید. ناوبری از آخرین نخ به اولین نخ و برعکس دور می‌زند (Wrap around).

+ / -

نرخ تازه‌سازی نمایشگر را افزایش یا کاهش دهید. بازه‌ی مجاز از ۰/۰۵ ثانیه (۲۰ هرتز، پاسخ‌دهی بسیار سریع) تا ۱/۰ ثانیه (۱ هرتز، سربار کمتر) است. نرخ‌های تازه‌سازی سریع‌تر از CPU بیشتری استفاده می‌کنند. مقدار پیش‌فرض ۰/۱ ثانیه (۱۰ هرتز) است.

x

شاخص‌های روندی را فعال یا غیرفعال کنید که نشان می‌دهند آیا توابع با گذشت زمان داغ‌تر می‌شوند یا خنک‌تر. وقتی این قابلیت فعال است، مقادیر رو به افزایش با رنگ سبز و مقادیر رو به کاهش با رنگ قرمز نمایش داده می‌شوند و هر به‌روزرسانی با به‌روزرسانی قبلی مقایسه می‌شود.

h or ?

صفحه‌ی راهنما را با تمام دستورات موجود نمایش دهید.

j / k (or Up / Down)

در میان ورودی‌های آپ‌کد در پنل آپ‌کد حرکت کنید (وقتی --opcodes فعال است). این کلیدها در میان آمارهای سطح دستورالعمل برای تابع انتخاب‌شده‌ی فعلی جابه‌جا می‌شوند.

هنگامی که پروفایل‌گیری به پایان می‌رسد (مدت زمان منقضی می‌شود یا فرایند هدف خارج می‌شود)، صفحه یک بنر با متن «PROFILING COMPLETE» نمایش می‌دهد و نتایج نهایی را متوقف می‌کند. همچنان می‌توانید پیش از فشردن کلید q برای خروج، در نتایج جابه‌جا شوید، آن‌ها را مرتب کنید و فیلتر اعمال کنید.

حالت زنده با گزینه‌های قالب خروجی (--collapsed, --flamegraph و غیره) سازگار نیست؛ زیرا به جای تولید خروجی پرونده، از یک رابط ترمینال تعاملی استفاده می‌کند.

پروفایل‌گیری آگاه از ناهمگامی

برای برنامه‌هایی که از asyncio استفاده می‌کنند، پروفایل‌گیر حالت آگاه از ناهمگام (--async-aware) را ارائه می‌دهد که پشته‌های فراخوانی را بر اساس ساختار وظیفه به‌جای فریم‌های خام پایتون بازسازی می‌کند:

python -m profiling.sampling run --async-aware async_script.py

پروفایل‌گیری استاندارد کد ناهمگام می‌تواند گیج‌کننده باشد زیرا پشته‌ی فراخوانی فیزیکی اغلب به‌جای جریان منطقی هم‌روال‌های شما، بخش‌های داخلی حلقه رویداد را نشان می‌دهد. حالت آگاه از ناهمگام با ردیابی اینکه کدام وظیفه در حال اجرا است و ارائه پشته‌هایی که زنجیره‌ی await را منعکس می‌کنند، به این مشکل رسیدگی می‌کند.

import asyncio

async def fetch(url):
    await asyncio.sleep(0.1)
    return url

async def main():
    for _ in range(50):
        await asyncio.gather(fetch("a"), fetch("b"), fetch("c"))

if __name__ == "__main__":
    asyncio.run(main())
python -m profiling.sampling run --async-aware --flamegraph -o out.html script.py

نکته

پروفایل‌گیری آگاه از ناهمگام مستلزم آن است که فرآیند هدف، ماژول asyncio را بارگذاری کرده باشد. اگر اسکریپتی را پیش از ایمپورت کردن asyncio پروفایل کنید، حالت آگاه از ناهمگام قادر نخواهد بود اطلاعات وظیفه را ثبت کند.

حالت‌های ناهمگام

گزینه‌ی --async-mode کنترل می‌کند که کدام وظایف در پروفایل ظاهر شوند:

python -m profiling.sampling run --async-aware --async-mode=running async_script.py
python -m profiling.sampling run --async-aware --async-mode=all async_script.py

با --async-mode=running (پیش‌فرض)، تنها وظیفه‌ای که در حال حاضر روی پردازنده اجرا می‌شود پروفایل می‌گردد. این کار نشان می‌دهد برنامه‌ی شما زمان خود را فعالانه کجا صرف می‌کند و انتخاب معمولی برای تحلیل کارایی است.

با --async-mode=all، وظایفی که معلق هستند (در انتظار ورودی/خروجی، قفل‌ها یا سایر وظایف) نیز گنجانده می‌شوند. این حالت برای درک اینکه برنامه‌ی شما منتظر چه چیزی است مفید است، اما از آنجا که هر وظیفه‌ی معلقی در هر نمونه ظاهر می‌شود، پروفایل‌های بزرگ‌تری تولید می‌کند.

نشانگرهای وظیفه و بازسازی پشته

در پروفایل‌های آگاه از ناهمگام، فریم‌های <task> را مشاهده خواهید کرد که مرزهای بین وظایف asyncio را مشخص می‌کنند. این‌ها فریم‌های مصنوعی هستند که توسط پروفایل‌گیر درج شده‌اند تا ساختار وظیفه را نشان دهند. نام وظیفه به عنوان نام تابع در این فریم‌ها ظاهر می‌شود.

هنگامی که یک وظیفه منتظر وظیفه‌ی دیگری است، پروفایل‌گیر زنجیره‌ی فراخوانی منطقی را با دنبال کردن روابط await بازسازی می‌کند. تنها وظایف «برگ» (وظایفی که هیچ وظیفه‌ی دیگری در حال حاضر منتظر آن‌ها نیست) ورودی‌های پشته‌ی خود را تولید می‌کنند. وظایفی که توسط وظایف دیگر مورد انتظار هستند، به‌جای آن به عنوان بخشی از پشته‌ی انتظارکشنده (awaiter) ظاهر می‌شوند.

اگر یک وظیفه چندین انتظارکشنده داشته باشد (یک الگوی لوزی در گراف وظیفه)، پروفایل‌گیر به روش قطعی یک والد را انتخاب می‌کند و نشانگر وظیفه را با تعداد والدین حاشیه‌نویسی می‌کند، برای مثال MyTask (2 parents). این نشان می‌دهد که مسیرهای اجرای جایگزین وجود دارند اما در این پشته‌ی خاص نمایش دادهچ نمی‌شوند.

محدودیت‌های گزینه

حالت آگاه از ناهمگام از مکانیسم بازسازی پشته‌ی متفاوتی استفاده می‌کند و با گزینه‌های زیر سازگار نیست: --native، --no-gc، --all-threads و --mode=cpu یا --mode=gil.

رابط خط فرمان

رابط خط فرمان کامل برای ارجاع.

گزینه‌های سراسری

run

یک اسکریپت یا ماژول پایتون را اجرا و پروفایل‌گیری کنید.

attach

با استفاده از PID به یک فرایند در حال اجرا متصل شوید و آن را پروفایل‌گیری کنید.

dump

یک تصویر لحظه‌ای تک‌باره از پشته‌ی پایتونِ یک فرایند در حال اجرا چاپ کنید.

replay

یک پرونده‌ی پروفایل دودویی را به قالب خروجی دیگری تبدیل کنید.

گزینه‌های برون‌ریزی

گزینه‌های زیر برای زیردستور dump اعمال می‌شوند:

-a, --all-threads

برون‌ریزی تمام نخ‌ها در فرایند هدف به جای فقط نخ اصلی.

--native

شامل کردن قاب‌های <native> برای کد غیرپایتون.

--no-gc

حذف کردن قاب‌های <GC> برای زباله‌روبی فعال.

--opcodes

نمایش نام‌های آپ‌کد بایت‌کد در صورت موجود بودن.

--async-aware

بازسازی پشته در سراسر مرزهای await برای برنامه‌های asyncio.

--async-mode <mode>

حالت پشته‌ی ناهمگام: running (فقط وظیفه‌ی در حال اجرا) یا all (تمام وظایف از جمله در حال انتظار). پیش‌فرض برای dump مقدار all است. به --async-aware نیاز دارد.

--blocking

مکث کردن تمام نخ‌ها در فرایند هدف هنگام خواندن پشته.

گزینه‌های نمونه‌برداری

-r <rate>, --sampling-rate <rate>

نرخ نمونه‌برداری (برای مثال، 10000، 10khz، 10k). پیش‌فرض: 1khz.

-d <seconds>, --duration <seconds>

مدت‌زمان پروفایل‌گیری بر حسب ثانیه. پیش‌فرض: اجرا تا اتمام.

-a, --all-threads

نمونه‌برداری از تمام نخ‌ها، نه فقط نخ اصلی.

--realtime-stats

نمایش آمارهای نمونه‌برداری در طول پروفایل‌گیری.

--native

شامل کردن قاب‌های <native> برای کد غیرپایتون.

--no-gc

حذف کردن قاب‌های <GC> برای زباله‌روبی.

--async-aware

فعال‌سازی پروفایل‌گیری آگاه از ناهمگامی برای برنامه‌های asyncio.

--opcodes

جمع‌آوری اطلاعات آپ‌کد بایت‌کد برای پروفایل‌گیری در سطح دستور. نشان می‌دهد که کدام دستورات بایت‌کد در حال اجرا هستند، از جمله تخصیص‌ها. فقط با قالب‌های --live، --flamegraph، --heatmap و --gecko سازگار است.

--subprocesses

همچنین پروفایل‌گیری زیرفرایندها. هر زیرفرایند نمونه و پرونده‌ی خروجی خودش را دریافت می‌کند. با --live سازگار نیست.

--blocking

متوقف کردن فرایند هدف در طول هر نمونه. این کار ردگیری پشته را به قیمت کند شدن هدف تضمین می‌کند. برای به حداقل رساندن تأثیر، با بازه‌های طولانی‌تر (۱۰۰۰ میکروثانیه یا بیشتر) استفاده کنید. برای جزئیات به حالت مسدودکننده مراجعه کنید.

گزینه‌های حالت

--mode <mode>

حالت نمونه‌برداری: wall (پیش‌فرض)، cpu، gil یا exception. حالت‌های cpu، gil و exception با --async-aware سازگار نیستند.

--async-mode <mode>

حالت پروفایل‌گیری ناهمگام: running (پیش‌فرض) یا all. نیازمند --async-aware است.

گزینه‌های خروجی

--pstats

تولید آمارهای pstats. این حالت پیش‌فرض است. هنگامی که در stdout نوشته شود، خروجی یک جدول متنی است؛ با -o، یک پرونده‌ی pstats دودویی است.

--collapsed

تولید قالب پشته‌ی فشرده‌شده برای ابزارهای نمودار شعله‌ی خارجی.

--flamegraph

تولید نمودار شعله‌ی HTML مستقل.

--diff-flamegraph <baseline.bin>

تولید نمودار شعله‌ی تفاضلی در مقایسه با یک پروفایل دودویی مبنا.

--gecko

تولید قالب JSON جکو (Gecko) برای پروفایل‌گیر فایرفاکس.

--heatmap

تولید نقشه‌ی حرارتی HTML با تعداد نمونه‌های در سطح سطر.

--binary

تولید قالب دودویی با کارایی بالا برای تبدیل بعدی با دستور replay.

--compression <type>

فشرده‌سازی برای قالب دودویی: auto (استفاده از zstd در صورت موجود بودن، پیش‌فرض)، zstd یا none.

-o <path>, --output <path>

مسیر پرونده یا پوشه‌ی خروجی. رفتار پیش‌فرض بسته به قالب متفاوت است: --pstats یک جدول متنی را در stdout چاپ می‌کند، در حالی که -o یک پرونده‌ی pstats دودویی می‌نویسد. سایر قالب‌ها پرونده‌ای با نام <format>_<PID>.<ext> تولید می‌کنند (برای نمونه، flamegraph_12345.html). --heatmap پوشه‌ای به نام heatmap_<PID> ایجاد می‌کند.

--browser

باز کردن خودکار خروجی HTML (--flamegraph و --heatmap) در مرورگر وب پیش‌فرض شما پس از تولید. هنگام پروفایل‌گیری با --subprocesses، فقط فرایند اصلی مرورگر را باز می‌کند؛ خروجی‌های زیرفرایند هرگز به طور خودکار باز نمی‌شوند.

گزینه‌های نمایش pstats

این گزینه‌ها فقط برای خروجی قالب pstats اعمال می‌شوند.

--sort <key>

ترتیب مرتب‌سازی: nsamples، tottime، cumtime، sample-pct، cumul-pct، nsamples-cumul یا name. پیش‌فرض: nsamples.

-l <count>, --limit <count>

حداکثر تعداد ورودی‌هایی که باید نمایش داده شوند. پیش‌فرض: ۱۵.

--no-summary

صرف‌نظر کردن از بخش‌های راهنما و خلاصه توابع جالب از خروجی.

گزینه‌های دستور اجرا

-m, --module

هدف را به عنوان نام یک ماژول در نظر بگیرید، نه به عنوان مسیر یک اسکریپت.

--live

شروع رابط ترمینال تعاملی به جای پروفایل‌گیری دسته‌ای.

همچنین ببینید

profiling

نمای کلی از ابزارهای پروفایل‌گیری پایتون و راهنمایی درباره‌ی انتخاب یک پروفایل‌گیر.

profiling.tracing

پروفایل‌گیر ردیابی قطعی برای تعداد دقیق فراخوانی‌ها و زمان‌بندی.

pstats

تحلیل آماری داده‌های پروفایل.

Firefox Profiler

پروفایل‌گیر مبتنی بر وب که خروجی با فرمت Gecko را می‌پذیرد. برای جزئیات استفاده، مستندات را ببینید.

FlameGraph

ابزارهایی برای تولید نمودارهای شعله (flame graphs) از فرمت پشته‌ی فشرده‌شده.