profile --- Pure Python profiler

Source code: Lib/profile.py


منسوخ شده از نسخه‌ی 3.15, در نسخه‌ی 3.17 حذف خواهد شد.

The profile module is deprecated and will be removed in Python 3.17. Use profiling.tracing instead.

The profile module provides a pure Python implementation of a deterministic profiler. While useful for understanding profiler internals or extending profiler behavior through subclassing, its pure Python implementation introduces significant overhead compared to the C-based profiling.tracing module.

For most profiling tasks, use:

Migration

Migrating from profile to profiling.tracing is straightforward. The APIs are compatible:

# Old (deprecated)
import profile
profile.run('my_function()')

# New (recommended)
import profiling.tracing
profiling.tracing.run('my_function()')

For most code, replacing import profile with import profiling.tracing (and using profiling.tracing instead of profile throughout) provides a straightforward migration path.

توجه

The cProfile module remains available as a backward-compatible alias to profiling.tracing. Existing code using import cProfile will continue to work without modification.

profile and profiling.tracing module reference

Both the profile and profiling.tracing modules provide the following functions:

profile.run(command, filename=None, sort=-1)

این تابع یک آرگومان واحد را که می‌توان آن را به تابع exec() ارسال کرد، و یک نام پرونده اختیاری می‌پذیرد. در همه موارد، این روال کد زیر را اجرا می‌کند:

exec(command, __main__.__dict__, __main__.__dict__)

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

profile.runctx(command, globals, locals, filename=None, sort=-1)

این تابع مشابه run() است، با آرگومان‌های اضافه‌ای برای فراهم‌کردن نگاشت‌های globals و locals برای رشته‌ی command. این روال اجرا می‌کند:

exec(command, globals, locals)

و آمارهای پروفایل‌گیری را همان‌طور که در تابع run() بالا آمده است، جمع‌آوری می‌کند.

class profile.Profile(timer=None, timeunit=0.0, subcalls=True, builtins=True)

This class is normally only used if more precise control over profiling is needed than what the profiling.tracing.run() function provides.

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

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

import profiling.tracing
import pstats
import io
from pstats import SortKey

pr = profiling.tracing.Profile()
pr.enable()
# ... do something ...
pr.disable()
s = io.StringIO()
sortby = SortKey.CUMULATIVE
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print(s.getvalue())

The Profile class can also be used as a context manager (supported only in profiling.tracing, not in the deprecated profile module; see انواع مدیر زمینه):

import profiling.tracing

with profiling.tracing.Profile() as pr:
    # ... do something ...

    pr.print_stats()

تغییر یافته در نسخه‌ی 3.8: پشتیبانی از مدیر زمینه افزوده شد.

enable()

Start collecting profiling data. Only in profiling.tracing.

disable()

Stop collecting profiling data. Only in profiling.tracing.

create_stats()

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

print_stats(sort=-1)

یک شیء Stats بر اساس پروفایل فعلی ایجاد کنید و نتایج را در stdout چاپ کنید.

The sort parameter specifies the sorting order of the displayed statistics. It accepts a single key or a tuple of keys to enable multi-level sorting, as in pstats.Stats.sort_stats().

اضافه شده در نسخه‌ی 3.13: print_stats() اکنون یک تاپل از کلیدها را می‌پذیرد.

dump_stats(filename)

نتایج پروفایل فعلی را در filename بنویسید.

run(cmd)

cmd را از طریق exec() پروفایل کنید

runctx(cmd, globals, locals)

cmd را از طریق exec() با محیط سراسری و محلی مشخص‌شده پروفایل کنید

runcall(func, /, *args, **kwargs)

func(*args, **kwargs) را پروفایل می‌کند

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

Differences from profiling.tracing

The profile module differs from profiling.tracing in several ways:

Higher overhead. The pure Python implementation is significantly slower than the C implementation, making it unsuitable for profiling long-running programs or performance-sensitive code.

Calibration support. The profile module supports calibration to compensate for profiling overhead. This is not needed in profiling.tracing because the C implementation has negligible overhead.

Custom timers. Both modules support custom timers, but profile accepts timer functions that return tuples (like os.times()), while profiling.tracing requires a function returning a single number.

Subclassing. The pure Python implementation is easier to subclass and extend for custom profiling behavior.

What is deterministic profiling?

Deterministic profiling is meant to reflect the fact that all function call, function return, and exception events are monitored, and precise timings are made for the intervals between these events (during which time the user's code is executing). In contrast, statistical profiling (which is provided by the profiling.sampling module) periodically samples the effective instruction pointer, and deduces where time is being spent. The latter technique traditionally involves less overhead (as the code does not need to be instrumented), but provides only relative indications of where time is being spent.

در پایتون، از آن‌جا که در حین اجرا یک مفسر فعال است، برای انجام پروفایل‌گیری قطعی (deterministic profiling) نیازی به وجود کد ابزاربندی‌شده (instrumented code) نیست. پایتون به‌طور خودکار یک hook (کال‌بک اختیاری) برای هر رویداد فراهم می‌کند. علاوه بر این، ماهیت تفسیری پایتون معمولاً آن‌قدر سربار زیادی به اجرا اضافه می‌کند، به‌طوری که پروفایل‌گیری قطعی معمولاً فقط سربار پردازشی اندکی در برنامه‌های کاربردی معمولی اضافه می‌کند. نتیجه این است که پروفایل‌گیری قطعی چندان پرهزینه نیست، اما آمار گسترده‌ای از زمان اجرا درباره اجرای یک برنامه پایتون ارائه می‌دهد.

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

محدودیت‌ها

یکی از محدودیت‌ها به دقت اطلاعات زمان‌بندی مربوط می‌شود. در پروفایل‌گیرهای قطعی (deterministic profilers) مشکلی بنیادین در مورد دقت وجود دارد. واضح‌ترین محدودیت این است که «ساعت» زیربنایی تنها با نرخی (معمولاً) حدود ۰٫۰۰۱ ثانیه تیک می‌زند. بنابراین، هیچ اندازه‌گیری‌ای دقیق‌تر از ساعت زیربنایی نخواهد بود. اگر تعداد کافی اندازه‌گیری انجام شود، «خطا» تمایل دارد به‌طور میانگین خنثی شود. متأسفانه، حذف این خطای نخست، منبع دوم خطا را به وجود می‌آورد.

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

The problem is more important with the deprecated profile module than with the lower-overhead profiling.tracing. For this reason, profile provides a means of calibrating itself for a given platform so that this error can be probabilistically (on the average) removed. After the profiler is calibrated, it will be more accurate (in a least square sense), but it will sometimes produce negative numbers (when call counts are exceptionally low, and the gods of probability work against you :-). ) Do not be alarmed by negative numbers in the profile. They should only appear if you have calibrated your profiler, and the results are actually better than without calibration.

کالیبراسیون

The profiler of the profile module subtracts a constant from each event handling time to compensate for the overhead of calling the time function, and socking away the results. By default, the constant is 0. The following procedure can be used to obtain a better constant for a given platform (see محدودیت‌ها).

import profile
pr = profile.Profile()
for i in range(5):
    print(pr.calibrate(10000))

این متد تعداد فراخوانی‌های پایتون مشخص‌شده توسط آرگومان را یک بار به‌طور مستقیم و بار دیگر تحت پروفایل‌گیر اجرا می‌کند و زمان هر دو را اندازه‌گیری می‌کند. سپس سربار پنهان به‌ازای هر رویداد پروفایل‌گیر را محاسبه می‌کند و آن را به‌عنوان یک عدد اعشاری بازمی‌گرداند. برای مثال، روی یک Intel Core i5 با فرکانس ۱٫۸ گیگاهرتز که macOS را اجرا می‌کند و با استفاده از time.process_time() پایتون به‌عنوان زمان‌سنج، عدد جادویی حدود ۴٫۰۴e-۶ است.

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

هنگامی که پاسخ سازگاری دارید، ۳ روش برای استفاده از آن وجود دارد:

import profile

# 1. Apply computed bias to all Profile instances created hereafter.
profile.Profile.bias = your_computed_bias

# 2. Apply computed bias to a specific Profile instance.
pr = profile.Profile()
pr.bias = your_computed_bias

# 3. Specify computed bias in instance constructor.
pr = profile.Profile(bias=your_computed_bias)

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

استفاده از زمان‌سنج سفارشی

اگر می‌خواهید نحوه تعیین زمان جاری را تغییر دهید (برای مثال، برای اجبار به استفاده از زمان دیواری یا زمان سپری‌شده فرایند)، تابع زمان‌سنجی مورد نظر خود را به سازنده کلاس Profile ارسال کنید:

pr = profile.Profile(your_time_func)

The resulting profiler will then call your_time_func. Depending on whether you are using profile.Profile or profiling.tracing.Profile, your_time_func's return value will be interpreted differently:

profile.Profile

your_time_func باید یک عدد زمانی واحد، یا فهرستی از اعداد را برگرداند که مجموع آن‌ها زمان جاری است (مانند آنچه os.times() برمی‌گرداند). اگر تابع یک عدد زمانی واحد برگرداند، یا فهرست اعداد برگردانده‌شده طول ۲ داشته باشد، نسخه‌ای به‌ویژه سریع از روال اعزام (dispatch routine) دریافت خواهید کرد.

توجه داشته باشید که باید کلاس پروفایل‌گیر را برای تابع زمان‌سنجی که انتخاب می‌کنید کالیبره کنید (به کالیبراسیون مراجعه کنید). در بیشتر ماشین‌ها، زمان‌سنجی که تنها یک مقدار عدد صحیح برمی‌گرداند، بهترین نتایج را از نظر سربار کم در حین پروفایل‌کردن فراهم می‌کند. (os.times() نسبتاً بد است، زیرا یک تاپل از مقادیر ممیز شناور برمی‌گرداند). اگر می‌خواهید یک زمان‌سنج بهتر را به تمیزترین روش جایگزین کنید، یک کلاس مشتق کنید و یک متد اعزام (dispatch) جایگزین را که فراخوانی زمان‌سنج شما را به بهترین شکل مدیریت می‌کند، به‌همراه ثابت کالیبراسیون مناسب، به‌صورت سخت‌کد (hardwire) پیاده‌سازی کنید.

profiling.tracing.Profile

your_time_func باید یک عدد واحد برگرداند. اگر این تابع اعداد صحیح برگرداند، همچنین می‌توانید سازنده‌ی کلاس را با یک آرگومان دوم برای مشخص کردن مدت‌زمان واقعی یک واحد زمان فراخوانی کنید. برای مثال، اگر your_integer_time_func مقادیر زمانی را برگرداند که بر حسب هزاران ثانیه اندازه‌گیری شده‌اند، باید نمونه‌ی Profile را به‌صورت زیر بسازید:

pr = profiling.tracing.Profile(your_integer_time_func, 0.001)

As the profiling.tracing.Profile class cannot be calibrated, custom timer functions should be used with care and should be as fast as possible. For the best results with a custom timer, it might be necessary to hard-code it in the C source of the internal _lsprof module.

پایتون 3.3 چندین تابع جدید به time اضافه می‌کند که می‌توان از آن‌ها برای اندازه‌گیری‌های دقیق زمان فرایند یا زمان دیواری استفاده کرد. برای مثال، time.perf_counter() را ببینید.

همچنین ملاحظه نمائید

profiling

Overview of Python profiling tools.

profiling.tracing

Recommended replacement for this module.

pstats

Statistical analysis and formatting for profile data.