# Профайлери Python

Source code: Lib/profile.py, Lib/pstats.py, and Lib/profile/sample.py

## Знайомство з профайлерами

Python provides both statistical profiling and deterministic profiling of Python programs. A profile is a set of statistics that describes how often and for how long various parts of the program executed. These statistics can be formatted into reports via the `pstats` module.

The Python standard library provides three different profiling implementations:

Statistical Profiler:

1. `profiling.sampling` provides statistical profiling of running Python processes using periodic stack sampling. It can attach to any running Python process without requiring code modification or restart, making it ideal for production debugging.

Deterministic Profilers:

2. `cProfile` is recommended for development and testing; it’s a C extension with reasonable overhead that makes it suitable for profiling long-running programs. Based on `lsprof`, contributed by Brett Rosen and Ted Czotter.

3. `profile`, чистий модуль Python, інтерфейс якого імітується `cProfile`, але який додає значні накладні витрати на профільовані програми. Якщо ви намагаєтеся якимось чином розширити профайлер, завдання може бути легшим із цим модулем. Спочатку розроблений і написаний Джимом Роскіндом.

Примітка

Модулі профайлера призначені для надання профілю виконання для даної програми, а не для порівняльного аналізу (для цього існує `timeit` для достатньо точних результатів). Це особливо стосується порівняння коду Python із кодом C: профайлери вводять додаткові витрати для коду Python, але не для функцій рівня C, тому код C здається швидшим за будь-який Python.

Profiler Comparison:

Feature | Statistical (`profiling.sampling`) | Deterministic (`cProfile`) | Deterministic (`profile`)
--- | --- | --- | ---
Target | Running process | Code you run | Code you run
Overhead | Virtually none | Moderate | High
Accuracy | Statistical approx. | Exact call counts | Exact call counts
Setup | Attach to any PID | Instrument code | Instrument code
Use Case | Production debugging | Development/testing | Profiler extension
Implementation | C extension | C extension | Pure Python

Примітка

The statistical profiler (`profiling.sampling`) is recommended for most production use cases due to its extremely low overhead and ability to profile running processes without modification. It can attach to any Python process and collect performance data with minimal impact on execution speed, making it ideal for debugging performance issues in live applications.

## What Is Statistical Profiling?

Statistical profiling works by periodically interrupting a running program to capture its current call stack. Rather than monitoring every function entry and exit like deterministic profilers, it takes snapshots at regular intervals to build a statistical picture of where the program spends its time.

The sampling profiler uses process memory reading (via system calls like `process_vm_readv` on Linux, `vm_read` on macOS, and `ReadProcessMemory` on Windows) to attach to a running Python process and extract stack trace information without requiring any code modification or restart of the target process. This approach provides several key advantages over traditional profiling methods.

The fundamental principle is that if a function appears frequently in the collected stack samples, it is likely consuming significant CPU time. By analyzing thousands of samples, the profiler can accurately estimate the relative time spent in different parts of the program. The statistical nature means that while individual measurements may vary, the aggregate results converge to represent the true performance characteristics of the application.

Since statistical profiling operates externally to the target process, it introduces virtually no overhead to the running program. The profiler process runs separately and reads the target process memory without interrupting its execution. This makes it suitable for profiling production systems where performance impact must be minimized.

The accuracy of statistical profiling improves with the number of samples collected. Short-lived functions may be missed or underrepresented, while long-running functions will be captured proportionally to their execution time. This characteristic makes statistical profiling particularly effective for identifying the most significant performance bottlenecks rather than providing exhaustive coverage of all function calls.

Statistical profiling excels at answering questions like «which functions consume the most CPU time?» and «where should I focus optimization efforts?» rather than «exactly how many times was this function called?» The trade-off between precision and practicality makes it an invaluable tool for performance analysis in real-world applications.

## Посібник користувача Instant

Цей розділ призначений для користувачів, які «не хочуть читати посібник». Він надає дуже короткий огляд і дозволяє користувачеві швидко виконувати профілювання існуючої програми.

Statistical Profiling (Recommended for Production):

To profile an existing running process:

    python -m profiling.sampling 1234

To profile with custom settings:

    python -m profiling.sampling -i 50 -d 30 1234

Deterministic Profiling (Development/Testing):

Для профілювання функції, яка приймає один аргумент, ви можете зробити:

    import cProfile
    import re
    cProfile.run('re.compile("foo|bar")')

(Використовуйте `profile` замість `cProfile`, якщо останній недоступний у вашій системі.)

Наведена вище дія запустить `re.compile()` і надрукує результати профілю, як показано нижче:

          214 function calls (207 primitive calls) in 0.002 seconds

    Ordered by: cumulative time

    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
         1    0.000    0.000    0.002    0.002 {built-in method builtins.exec}
         1    0.000    0.000    0.001    0.001 <string>:1(<module>)
         1    0.000    0.000    0.001    0.001 __init__.py:250(compile)
         1    0.000    0.000    0.001    0.001 __init__.py:289(_compile)
         1    0.000    0.000    0.000    0.000 _compiler.py:759(compile)
         1    0.000    0.000    0.000    0.000 _parser.py:937(parse)
         1    0.000    0.000    0.000    0.000 _compiler.py:598(_code)
         1    0.000    0.000    0.000    0.000 _parser.py:435(_parse_sub)

The first line indicates that 214 calls were monitored. Of those calls, 207 were primitive, meaning that the call was not induced via recursion. The next line: `Ordered by: cumulative time` indicates the output is sorted by the `cumtime` values. The column headings include:

виклики

  за кількість дзвінків.

tottime

  для загального часу, витраченого на дану функцію (за винятком часу, витраченого на виклики підфункцій)

percall

  це частка `tottime`, поділена на `ncalls`

cumtime

  це сукупний час, витрачений на цю та всі підфункції (від виклику до виходу). Ця цифра точна навіть для рекурсивних функцій.

percall

  це частка `cumtime`, поділена на примітивні виклики

ім’я файлу: lineno(функція)

  надає відповідні дані кожної функції

Якщо в першому стовпці є два числа (наприклад, `3/1`), це означає, що функція рекурсувала. Друге значення — це кількість первинних викликів, а перше — загальна кількість викликів. Зауважте, що коли функція не рекурсує, ці два значення однакові, і друкується лише одна цифра.

Замість того, щоб друкувати вихідні дані в кінці запуску профілю, ви можете зберегти результати у файлі, вказавши ім’я файлу у функції `run()`:

    import cProfile
    import re
    cProfile.run('re.compile("foo|bar")', 'restats')

Клас `pstats.Stats` читає результати профілю з файлу та форматує їх різними способами.

## Statistical Profiler Command Line Interface

The `profiling.sampling` module can be invoked as a script to profile running processes:

    python -m profiling.sampling [options] PID

Basic Usage Examples:

Profile process 1234 for 10 seconds with default settings:

    python -m profiling.sampling 1234

Profile with custom interval and duration, save to file:

    python -m profiling.sampling -i 50 -d 30 -o profile.stats 1234

Generate collapsed stacks to use with tools like flamegraph.pl:

    python -m profiling.sampling --collapsed 1234

Profile all threads, sort by total time:

    python -m profiling.sampling -a --sort-tottime 1234

Profile with real-time sampling statistics:

    python -m profiling.sampling --realtime-stats 1234

Command Line Options:

PID

  Process ID of the Python process to profile (required)

-i, --interval INTERVAL

  Sampling interval in microseconds (default: 100)

-d, --duration DURATION

  Sampling duration in seconds (default: 10)

-a, --all-threads

  Sample all threads in the process instead of just the main thread

--realtime-stats

  Print real-time sampling statistics during profiling

--pstats

  Generate pstats output (default)

--collapsed

  Generate collapsed stack traces for flamegraphs

-o, --outfile OUTFILE

  Save output to a file

Sorting Options (pstats format only):

--sort-nsamples

  Sort by number of direct samples

--sort-tottime

  Sort by total time

--sort-cumtime

  Sort by cumulative time (default)

--sort-sample-pct

  Sort by sample percentage

--sort-cumul-pct

  Sort by cumulative sample percentage

--sort-nsamples-cumul

  Sort by cumulative samples

--sort-name

  Sort by function name

-l, --limit LIMIT

  Limit the number of rows in the output (default: 15)

--no-summary

  Disable the summary section in the output

Understanding Statistical Profile Output:

The statistical profiler produces output similar to deterministic profilers but with different column meanings:

    Profile Stats:
           nsamples  sample%     tottime (ms)  cumul%    cumtime (ms)  filename:lineno(function)
              45/67     12.5        23.450     18.6        56.780     mymodule.py:42(process_data)
              23/23      6.4        15.230      6.4        15.230     <built-in>:0(len)

Column Meanings:

- nsamples: `direct/cumulative` - Times function was directly executing / on call stack

- sample%: Percentage of total samples where function was directly executing

- tottime: Estimated time spent directly in this function

- cumul%: Percentage of samples where function was anywhere on call stack

- cumtime: Estimated cumulative time including called functions

- filename:lineno(function): Location and name of the function

## `profiling.sampling` Module Reference

This section documents the programmatic interface for the `profiling.sampling` module. For command-line usage, see Statistical Profiler Command Line Interface. For conceptual information about statistical profiling, see What Is Statistical Profiling?

profiling.sampling.sample(pid, *, sort=2, sample_interval_usec=100, duration_sec=10, filename=None, all_threads=False, limit=None, show_summary=True, output_format='pstats', realtime_stats=False)

  Sample a Python process and generate profiling data.

  This is the main entry point for statistical profiling. It creates a `SampleProfiler`, collects stack traces from the target process, and outputs the results in the specified format.

  Параметри:

    - pid (int) – Process ID of the target Python process

    - sort (int) – Sort order for pstats output (default: 2 for cumulative time)

    - sample_interval_usec (int) – Sampling interval in microseconds (default: 100)

    - duration_sec (int) – Duration to sample in seconds (default: 10)

    - filename (str) – Output filename (None for stdout/default naming)

    - all_threads (bool) – Whether to sample all threads (default: False)

    - limit (int) – Maximum number of functions to display (default: None)

    - show_summary (bool) – Whether to show summary statistics (default: True)

    - output_format (str) – Output format - „pstats“ or „collapsed“ (default: „pstats“)

    - realtime_stats (bool) – Whether to display real-time statistics (default: False)

  Викликає:

    ValueError – If output_format is not „pstats“ or „collapsed“

  Examples:

      # Basic usage - profile process 1234 for 10 seconds
      import profiling.sampling
      profiling.sampling.sample(1234)

      # Profile with custom settings
      profiling.sampling.sample(1234, duration_sec=30, sample_interval_usec=50, all_threads=True)

      # Generate collapsed stack traces for flamegraph.pl
      profiling.sampling.sample(1234, output_format='collapsed', filename='profile.collapsed')

class profiling.sampling.SampleProfiler(pid, sample_interval_usec, all_threads)

  Low-level API for the statistical profiler.

  This profiler uses periodic stack sampling to collect performance data from running Python processes with minimal overhead. It can attach to any Python process by PID and collect stack traces at regular intervals.

  Параметри:

    - pid (int) – Process ID of the target Python process

    - sample_interval_usec (int) – Sampling interval in microseconds

    - all_threads (bool) – Whether to sample all threads or just the main thread

  sample(collector, duration_sec=10)

    Sample the target process for the specified duration.

    Collects stack traces from the target process at regular intervals and passes them to the provided collector for processing.

    Параметри:

      - collector – Object that implements `collect()` method to process stack traces

      - duration_sec (int) – Duration to sample in seconds (default: 10)

    The method tracks sampling statistics and can display real-time information if realtime_stats is enabled.

Дивись також

Statistical Profiler Command Line Interface

  Command-line interface documentation for the statistical profiler.

## Deterministic Profiler Command Line Interface

Файли `cProfile` і `profile` також можна викликати як сценарій для профілювання іншого сценарію. Наприклад:

    python -m cProfile [-o output_file] [-s sort_order] (-m module | myscript.py)

-o <output_file>

  Writes the profile results to a file instead of to stdout.

-s <sort_order>

  Specifies one of the `sort_stats()` sort values to sort the output by. This only applies when `-o` is not supplied.

-m <module>

  Specifies that a module is being profiled instead of a script.

  Added in version 3.7: Додано опцію `-m` до `cProfile`.

  Added in version 3.8: Додано опцію `-m` до `profile`.

Клас `Stats` модуля `pstats` має різноманітні методи для обробки та друку даних, збережених у файлі результатів профілю:

    import pstats
    from pstats import SortKey
    p = pstats.Stats('restats')
    p.strip_dirs().sort_stats(-1).print_stats()

Метод `strip_dirs()` видалив зайвий шлях з усіх імен модулів. Метод `sort_stats()` відсортував усі записи відповідно до стандартного рядка модуля/рядка/назви, який друкується. Метод `print_stats()` виводить всю статистику. Ви можете спробувати такі виклики сортування:

    p.sort_stats(SortKey.NAME)
    p.print_stats()

Перший виклик фактично відсортує список за назвою функції, а другий виклик роздрукує статистику. Нижче наведено кілька цікавих викликів для експериментів:

    p.sort_stats(SortKey.CUMULATIVE).print_stats(10)

Це сортує профіль за сукупним часом у функції, а потім друкує лише десять найважливіших рядків. Якщо ви хочете зрозуміти, які алгоритми потребують часу, ви б використали рядок вище.

Якби ви хотіли побачити, які функції часто зациклюються та займають багато часу, ви б зробили:

    p.sort_stats(SortKey.TIME).print_stats(10)

щоб відсортувати відповідно до часу, витраченого на виконання кожної функції, а потім надрукувати статистику для перших десяти функцій.

Ви також можете спробувати:

    p.sort_stats(SortKey.FILENAME).print_stats('__init__')

Це відсортує всю статистику за назвою файлу, а потім роздрукує статистику лише для методів ініціалізації класу (оскільки в них пишеться `__init__`). Як останній приклад, ви можете спробувати:

    p.sort_stats(SortKey.TIME, SortKey.CUMULATIVE).print_stats(.5, 'init')

Цей рядок сортує статистичні дані за первинним ключем часу та вторинним ключем сукупного часу, а потім роздруковує деякі статистичні дані. Щоб бути конкретним, список спочатку відбирається до 50% (re: `.5`) від його початкового розміру, потім зберігаються лише рядки, що містять `init`, і цей підпідсписок друкується.

Якщо вам було цікаво, які функції називають наведені вище функції, ви можете тепер (`p` все ще сортується за останнім критерієм) зробити:

    p.print_callers(.5, 'init')

і ви отримаєте список абонентів для кожної з перелічених функцій.

Якщо ви хочете отримати більше функціональних можливостей, вам доведеться прочитати посібник або здогадатися, що роблять такі функції:

    p.print_callees()
    p.add('restats')

Викликаний як скрипт, модуль `pstats` є браузером статистики для читання та вивчення дампів профілю. Він має простий рядково-орієнтований інтерфейс (реалізований за допомогою `cmd`) та інтерактивну довідку.

## Довідка про модуль `profile` і `cProfile`

Обидва модулі `profile` і `cProfile` забезпечують такі функції:

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)

  This function is similar to `run()`, with added arguments to supply the globals and locals mappings for the command string. This routine executes:

      exec(command, globals, locals)

  і збирає статистику профілювання, як у функції `run()` вище.

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

  Зазвичай цей клас використовується, лише якщо потрібен більш точний контроль над профілюванням, ніж той, який забезпечує функція `cProfile.run()`.

  Можна надати власний таймер для вимірювання тривалості виконання коду за допомогою аргументу timer. Це має бути функція, яка повертає одне число, що відповідає поточному часу. Якщо число є цілим числом, timeunit визначає множник, який визначає тривалість кожної одиниці часу. Наприклад, якщо таймер повертає час, виміряний у тисячах секунд, одиницею часу буде `.001`.

  Безпосереднє використання класу `Profile` дозволяє форматувати результати профілю без запису даних профілю у файл:

      import cProfile, pstats, io
      from pstats import SortKey
      pr = cProfile.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())

  Клас `Profile` також можна використовувати як менеджер контексту (підтримується лише в модулі `cProfile`. Див. Типи менеджера контексту):

      import cProfile

      with cProfile.Profile() as pr:
          # ... do something ...

          pr.print_stats()

  Змінено в версії 3.8: Додано підтримку менеджера контексту.

  enable()

    Почніть збирати дані профілювання. Тільки в `cProfile`.

  disable()

    Припиніть збір даних профілювання. Тільки в `cProfile`.

  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 `Stats.sort_stats`.

    Added in version 3.13: `print_stats()` now accepts a tuple of keys.

  dump_stats(filename)

    Запишіть результати поточного профілю в filename.

  run(cmd)

    Профілюйте cmd через `exec()`.

  runctx(cmd, globals, locals)

    Профілюйте cmd через `exec()` із зазначеним глобальним і локальним середовищем.

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

    Профіль `func(*args, **kwargs)`

Зауважте, що профілювання працюватиме, лише якщо викликана команда/функція дійсно повернеться. Якщо інтерпретатор завершується (наприклад, через виклик `sys.exit()` під час виконання викликаної команди/функції), результати профілювання не друкуються.

## Клас `Stats`

Аналіз даних профайлера виконується за допомогою класу `Stats`.

class pstats.Stats(*filenames or profile, stream=sys.stdout)

  Цей конструктор класу створює екземпляр «статистичного об’єкта» з імені файлу (або списку імен файлів) або з екземпляра `Profile`. Вихід буде надруковано в потік, указаний stream.

  Файл, вибраний конструктором вище, має бути створений відповідною версією `profile` або `cProfile`. Якщо бути конкретнішим, немає гарантії сумісності файлів із майбутніми версіями цього профайлера, а також немає сумісності з файлами, створеними іншими профайлерами, або тим самим профайлером, що працює в іншій операційній системі. Якщо надано кілька файлів, усі статистичні дані для ідентичних функцій будуть об’єднані, щоб загальний огляд кількох процесів можна було розглянути в одному звіті. Якщо додаткові файли потрібно об’єднати з даними в існуючому об’єкті `Stats`, можна використати метод `add()`.

  Замість читання даних профілю з файлу, об’єкт `cProfile.Profile` або `profile.Profile` можна використовувати як джерело даних профілю.

  Об’єкти `Stats` мають такі методи:

  strip_dirs()

    Цей метод для класу `Stats` видаляє всю інформацію про початковий шлях із імен файлів. Це дуже корисно для зменшення розміру роздруківки до (близько) 80 стовпців. Цей метод змінює об’єкт, і видалена інформація втрачається. Після виконання операції видалення вважається, що об’єкт має свої записи у «випадковому» порядку, як це було одразу після ініціалізації та завантаження об’єкта. Якщо `strip_dirs()` спричиняє нерозрізнення двох імен функцій (вони знаходяться в одному рядку того самого імені файлу та мають однакову назву функції), тоді статистика для цих двох записів накопичується в єдиний вхід.

  add(*filenames)

    Цей метод класу `Stats` накопичує додаткову інформацію профілювання в поточному об’єкті профілювання. Його аргументи мають посилатися на імена файлів, створені відповідною версією `profile.run()` або `cProfile.run()`. Статистика для функцій з ідентичними назвами (re: файл, рядок, ім’я) автоматично накопичується в одній статистиці функції.

  dump_stats(filename)

    Збережіть дані, завантажені в об’єкт `Stats`, у файл з назвою filename. Файл створюється, якщо він не існує, і перезаписується, якщо він уже існує. Це еквівалентно однойменному методу в класах `profile.Profile` і `cProfile.Profile`.

  sort_stats(*keys)

    Цей метод змінює об’єкт `Stats`, сортуючи його відповідно до наданих критеріїв. Аргументом може бути рядок або перелік SortKey, що визначає основу сортування (приклад: `'time'`, `'name'`, `SortKey.TIME` або `SortKey.NAME`). Аргумент переліку SortKey має перевагу над рядковим аргументом у тому, що він більш надійний і менш схильний до помилок.

    Якщо надається більше одного ключа, то додаткові ключі використовуються як вторинні критерії, якщо є рівність у всіх ключах, вибраних перед ними. Наприклад, `sort_stats(SortKey.NAME, SortKey.FILE)` відсортує всі записи відповідно до їхньої назви функції та розв’яже всі зв’язки (ідентичні назви функції) шляхом сортування за назвою файлу.

    Для рядкового аргументу можна використовувати абревіатури для будь-яких імен ключів, за умови, що скорочення є однозначним.

    Нижче наведено дійсний рядок і SortKey:

    Дійсний рядковий аргумент | Дійсний перелік Arg | Значення
    --- | --- | ---
    `'дзвінки'` | SortKey.CALLS | кількість дзвінків
    `'кумулятивний''` | SortKey.CUMULATIVE | кумулятивний час
    `'cumtime''` | N/A | кумулятивний час
    `''файл'` | N/A | ім’я файлу
    `'ім'я файлу'` | SortKey.FILENAME | ім’я файлу
    `''модуль'` | N/A | ім’я файлу
    `'calls''` | N/A | кількість дзвінків
    `'pcalls'` | SortKey.PCALLS | примітивний підрахунок викликів
    `''рядок'` | SortKey.LINE | номер рядка
    `''ім'я'` | SortKey.NAME | назва функції
    `'nfl'` | SortKey.NFL | ім’я/файл/рядок
    `'stdname'` | SortKey.STDNAME | стандартна назва
    `'час''` | SortKey.TIME | внутрішній час
    `'tottime'` | N/A | внутрішній час

    Зауважте, що всі сортування в статистиці виконуються в порядку спадання (першими розміщуються елементи, що вимагають більше часу), а пошук імені, файлу та номера рядка здійснюється в порядку зростання (за алфавітом). Тонка різниця між `SortKey.NFL` і `SortKey.STDNAME` полягає в тому, що стандартна назва є різновидом назви, як надруковано, що означає, що вбудовані номери рядків порівнюються дивним чином. Наприклад, рядки 3, 20 і 40 (якби імена файлів були однаковими) відображалися б у порядку рядків 20, 3 і 40. На відміну від цього, `SortKey.NFL` виконує числове порівняння номерів рядків. Фактично, `sort_stats(SortKey.NFL)` те саме, що `sort_stats(SortKey.NAME, SortKey.FILENAME, SortKey.LINE)`.

    З міркувань зворотної сумісності дозволені числові аргументи `-1`, `0`, `1` і `2`. Вони інтерпретуються як `'stdname'`, `'calls'`, `'time'` і `'cumulative'` відповідно. Якщо використовується цей формат старого стилю (числовий), використовуватиметься лише один ключ сортування (числовий ключ), а додаткові аргументи мовчки ігноруватимуться.

    Added in version 3.7: Додано перелік SortKey.

  reverse_order()

    Цей метод для класу `Stats` змінює порядок базового списку в об’єкті на протилежний. Зверніть увагу, що за замовчуванням порядок зростання чи спадання вибрано належним чином на основі вибраного ключа сортування.

  print_stats(*restrictions)

    Цей метод для класу `Stats` друкує звіт, як описано у визначенні `profile.run()`.

    Порядок друку базується на останній операції `sort_stats()`, виконаній над об’єктом (з урахуванням застережень у `add()` і `strip_dirs()`).

    The arguments provided (if any) can be used to limit the list down to the significant entries. Initially, the list is taken to be the complete set of profiled functions. Each restriction is either an integer (to select a count of lines), or a decimal fraction between 0.0 and 1.0 inclusive (to select a percentage of lines), or a string that will be interpreted as a regular expression (to pattern match the standard name that is printed). If several restrictions are provided, then they are applied sequentially. For example:

        print_stats(.1, 'foo:')

    спочатку обмежує друк до перших 10% списку, а потім друкує лише функції, які є частиною імені файлу `.*foo:`. Навпаки, команда:

        print_stats('foo:', .1)

    обмежить список усіма функціями, що мають імена файлів `.*foo:`, а потім перейде до друку лише перших 10% із них.

  print_callers(*restrictions)

    Цей метод для класу `Stats` друкує список усіх функцій, які викликали кожну функцію в профільованій базі даних. Порядок ідентичний тому, який надає `print_stats()`, і визначення обмежувального аргументу також ідентичне. Кожен абонент повідомляється на своїй лінії. Формат дещо відрізняється залежно від профайлера, який створив статистику:

    - За допомогою `profile` число відображається в дужках після кожного абонента, щоб показати, скільки разів було зроблено цей конкретний виклик. Для зручності друге число без дужок повторює сукупний час, витрачений на функцію праворуч.

    - За допомогою `cProfile` перед кожним викликом стоять три числа: кількість разів, коли цей конкретний виклик було здійснено, а також загальний і сукупний час, витрачений на поточну функцію під час її виклику цим конкретним викликом.

  print_callees(*restrictions)

    Цей метод для класу `Stats` друкує список усіх функцій, викликаних зазначеною функцією. Окрім зміни напрямку викликів (re: called проти was called by), аргументи та порядок ідентичні методу `print_callers()`.

  get_stats_profile()

    Цей метод повертає екземпляр StatsProfile, який містить зіставлення імен функцій з екземплярами FunctionProfile. Кожен екземпляр FunctionProfile містить інформацію, пов’язану з профілем функції, наприклад, скільки часу знадобилося для виконання функції, скільки разів її викликали тощо…

    Added in version 3.9: Додано такі класи даних: StatsProfile, FunctionProfile. Додано таку функцію: get_stats_profile.

## Що таке детерміноване профілювання?

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.

У Python, оскільки під час виконання активний інтерпретатор, наявність інструментального коду не потрібна для виконання детермінованого профілювання. Python автоматично надає hook (додатковий зворотний виклик) для кожної події. Крім того, інтерпретована природа Python має тенденцію додавати стільки накладних витрат на виконання, що детерміноване профілювання має тенденцію лише додавати невеликі накладні витрати на обробку в типових програмах. Результатом є те, що детерміноване профілювання не таке вже й дороге, але забезпечує розширену статистику часу виконання про виконання програми Python.

Статистику кількості викликів можна використовувати для виявлення помилок у коді (несподівана кількість) і для визначення можливих точок вбудованого розширення (висока кількість викликів). Внутрішню статистику часу можна використовувати для виявлення «гарячих циклів», які слід ретельно оптимізувати. Кумулятивну статистику часу слід використовувати для виявлення помилок високого рівня у виборі алгоритмів. Зауважте, що незвичайна обробка сукупного часу в цьому профайлері дозволяє прямо порівнювати статистику для рекурсивних реалізацій алгоритмів з ітеративними реалізаціями.

## Обмеження

Одне обмеження пов’язане з точністю інформації про час. Існує фундаментальна проблема з детермінованим профайлером, пов’язана з точністю. Найбільш очевидним обмеженням є те, що основний «годинник» цокає лише зі швидкістю (зазвичай) приблизно 0,001 секунди. Тому жодні вимірювання не будуть точнішими, ніж базовий годинник. Якщо проведено достатньо вимірювань, «похибка» матиме тенденцію до усереднення. На жаль, видалення цієї першої помилки спричиняє друге джерело помилки.

Друга проблема полягає в тому, що «потрібен деякий час» від моменту надсилання події до моменту, коли виклик профайлера для отримання часу фактично отримує стан годинника. Подібним чином існує певна затримка під час виходу з обробника подій профайлера з моменту, коли було отримано значення годинника (і потім вилучено), до моменту, коли код користувача знову виконується. У результаті функції, які викликаються багато разів або викликають багато функцій, зазвичай накопичуватимуть цю помилку. Похибка, яка накопичується таким чином, зазвичай менша, ніж точність годинника (менше одного такту), але вона може накопичуватися і стати дуже значною.

Проблема важливіша з `profile`, ніж з нижчими накладними `cProfile`. З цієї причини `profile` забезпечує засіб самокалібрування для певної платформи, щоб цю помилку можна було ймовірно (в середньому) усунути. Після калібрування профайлера він буде більш точним (у сенсі найменших квадратів), але іноді він видаватиме від’ємні числа (коли кількість викликів надзвичайно низька, і боги ймовірності працюють проти вас :-). ) Не лякайтеся негативних цифр в профілі. Вони мають з’являтися тільки, якщо ви відкалібрували свій профайлер, і результати насправді кращі, ніж без калібрування.

## Калібрування

Профайлер модуля `profile` віднімає константу від часу обробки кожної події, щоб компенсувати накладні витрати на виклик функції часу та накопичення результатів. За замовчуванням константа дорівнює 0. Наступну процедуру можна використати, щоб отримати кращу константу для даної платформи (див. Обмеження).

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

Метод виконує кількість викликів Python, заданих аргументом, безпосередньо та знову під профайлером, вимірюючи час для обох. Потім він обчислює приховані накладні витрати на кожну подію профілювача та повертає їх як число з плаваючою точкою. Наприклад, на процесорі Intel Core i5 з тактовою частотою 1,8 ГГц, який працює під керуванням macOS і використовує Python time.process_time() як таймер, магічне число становить приблизно 4,04e-6.

Мета цієї вправи — отримати досить послідовний результат. Якщо ваш комп’ютер дуже швидкий або ваша функція таймера має низьку роздільну здатність, вам, можливо, доведеться передати 100 000 або навіть 1000 000, щоб отримати стабільні результати.

Якщо у вас є послідовна відповідь, ви можете використовувати її трьома способами:

    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)

Потім отриманий профайлер викличе `your_time_func`. Залежно від того, чи використовуєте ви `profile.Profile` чи `cProfile.Profile`, значення, що повертається `your_time_func`, буде інтерпретуватися по-різному:

`профіль.Профіль`

  `your_time_func` має повертати одне число або список чисел, сума яких є поточним часом (як те, що повертає `os.times()`). Якщо функція повертає єдине число часу або список повернутих чисел має довжину 2, тоді ви отримаєте особливо швидку версію процедури відправки.

  Be warned that you should calibrate the profiler class for the timer function that you choose (see Калібрування). For most machines, a timer that returns a lone integer value will provide the best results in terms of low overhead during profiling. (`os.times()` is pretty bad, as it returns a tuple of floating-point values). If you want to substitute a better timer in the cleanest fashion, derive a class and hardwire a replacement dispatch method that best handles your timer call, along with the appropriate calibration constant.

`cProfile.Profile`

  `your_time_func` має повертати одне число. Якщо він повертає цілі числа, ви також можете викликати конструктор класу з другим аргументом, який визначає реальну тривалість однієї одиниці часу. Наприклад, якщо `your_integer_time_func` повертає час, виміряний у тисячах секунд, ви повинні створити екземпляр `Profile` наступним чином:

      pr = cProfile.Profile(your_integer_time_func, 0.001)

  As the `cProfile.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.

Python 3.3 додає кілька нових функцій у `time`, які можна використовувати для точного вимірювання часу процесу або настінного годинника. Наприклад, перегляньте `time.perf_counter()`.