# `profile` — Pure Python profiler

Source code: Lib/profile.py

Deprecated since version 3.15, will be removed in version 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:

- `profiling.sampling` for production debugging with zero overhead

- `profiling.tracing` for development and testing

## 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)

  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)

  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()`.

    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()` під час виконання викликаної команди/функції), результати профілювання не друкуються.

## 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.

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

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

## Обмеження

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

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

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))

Метод виконує кількість викликів 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)

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:

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

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

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

Python 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.