profile.md
1> **Источник:** https://python-all.ru/3.4/library/profile.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 27.4. Профилировщики Python89**Исходный код:** [Lib/profile.py](https://python-all.ru/src/3.4/Lib/profile.py) и [Lib/pstats.py](https://python-all.ru/src/3.4/Lib/pstats.py)1011---1213## 27.4.1. Введение в профилировщики1415[`cProfile`](https://python-all.ru/3.4/library/profile.html#module-cProfile) и [`profile`](https://python-all.ru/3.4/library/profile.html#module-profile) обеспечивают *детерминированное профилирование* программ Python. *Профиль* – это набор статистики, описывающей, как часто и как долго выполнялись различные части программы. Эту статистику можно преобразовать в отчёты с помощью модуля [`pstats`](https://python-all.ru/3.4/library/profile.html#module-pstats).1617Стандартная библиотека Python предоставляет две различные реализации одного и того же интерфейса профилирования:18191. Большинству пользователей рекомендуется [`cProfile`](https://python-all.ru/3.4/library/profile.html#module-cProfile); это расширение на C с умеренными накладными расходами, что делает его пригодным для профилирования долго работающих программ. Основан на `lsprof`, предоставленном Бреттом Розеном и Тедом Цоттером.202. [`profile`](https://python-all.ru/3.4/library/profile.html#module-profile) – это чистый модуль Python, чей интерфейс повторяет [`cProfile`](https://python-all.ru/3.4/library/profile.html#module-cProfile), но он вносит значительные накладные расходы в профилируемые программы. Если вы пытаетесь расширить профилировщик каким-либо образом, эта задача может оказаться проще с данным модулем.2122> **Примечание**23>24> Модули профилировщиков предназначены для получения профиля выполнения данной программы, а не для целей тестирования производительности (для этого существует [`timeit`](https://python-all.ru/3.4/library/timeit.html#module-timeit) для достаточно точных результатов). Это особенно касается сравнения производительности кода Python с кодом C: профилировщики вносят накладные расходы для кода Python, но не для функций уровня C, поэтому код C будет казаться быстрее любого кода Python.2526## 27.4.2. Краткое руководство пользователя2728Этот раздел предназначен для пользователей, которые «не хотят читать руководство». В нём даётся очень краткий обзор, и он позволяет быстро выполнить профилирование существующего приложения.2930Чтобы профилировать функцию, принимающую один аргумент, можно сделать следующее:3132```python33import cProfile34import re35cProfile.run('re.compile("foo|bar")')36```3738(Используйте [`profile`](https://python-all.ru/3.4/library/profile.html#module-profile) вместо [`cProfile`](https://python-all.ru/3.4/library/profile.html#module-cProfile), если последний недоступен в вашей системе.)3940Приведённая команда выполнит [`re.compile()`](https://python-all.ru/3.4/library/re.html#re.compile) и выведет результаты профилирования, как показано ниже:4142```python43 197 function calls (192 primitive calls) in 0.002 seconds4445Ordered by: standard name4647ncalls tottime percall cumtime percall filename:lineno(function)48 1 0.000 0.000 0.001 0.001 <string>:1(<module>)49 1 0.000 0.000 0.001 0.001 re.py:212(compile)50 1 0.000 0.000 0.001 0.001 re.py:268(_compile)51 1 0.000 0.000 0.000 0.000 sre_compile.py:172(_compile_charset)52 1 0.000 0.000 0.000 0.000 sre_compile.py:201(_optimize_charset)53 4 0.000 0.000 0.000 0.000 sre_compile.py:25(_identityfunction)54 3/1 0.000 0.000 0.000 0.000 sre_compile.py:33(_compile)55```5657Первая строка показывает, что было отслежено 197 вызовов. Из них 192 были *первичными*, то есть вызов не был вызван рекурсией. Следующая строка: `Ordered by: standard name`, указывает, что текстовая строка в крайнем правом столбце использовалась для сортировки вывода. Заголовки столбцов включают:5859**ncalls**6061– количество вызовов,6263**tottime**6465– общее время, затраченное в данной функции (исключая время, затраченное в вызовах подфункций).6667**percall**6869– результат деления7071`tottime`7273на7475`ncalls`7677**cumtime**7879– совокупное время, затраченное в этой и всех подфункциях (от вызова до выхода). Это значение точное8081*даже*8283для рекурсивных функций.8485**percall**8687– результат деления8889`cumtime`9091на количество первичных вызовов9293**filename:lineno(function)**9495– предоставляет соответствующие данные каждой функции9697Когда в первом столбце два числа (например, `3/1`), это означает, что функция выполняла рекурсию. Второе значение – количество примитивных вызовов, а первое – общее количество вызовов. Обратите внимание: если функция не рекурсивна, эти два значения совпадают, и печатается только одно число.9899Вместо вывода результатов в конце сеанса профилирования их можно сохранить в файл, передав имя файла функции `run()`:100101```python102import cProfile103import re104cProfile.run('re.compile("foo|bar")', 'restats')105```106107Класс [`pstats.Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats) читает результаты профилирования из файла и форматирует их различными способами.108109Файл [`cProfile`](https://python-all.ru/3.4/library/profile.html#module-cProfile) также можно запустить как скрипт для профилирования другого скрипта. Например:110111```python112python -m cProfile [-o output_file] [-s sort_order] myscript.py113```114115`-o` записывает результаты профилирования в файл вместо stdout116117`-s` задаёт одно из значений сортировки [`sort_stats()`](https://python-all.ru/3.4/library/profile.html#pstats.Stats.sort_stats) для упорядочивания вывода. Это действует только при отсутствии `-o`.118119Класс [`Stats`](https://python-all.ru/3.4/library/profile.html#module-pstats) из модуля [`pstats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats) имеет множество методов для обработки и вывода данных, сохранённых в файле результатов профилирования:120121```python122import pstats123p = pstats.Stats('restats')124p.strip_dirs().sort_stats(-1).print_stats()125```126127The [`strip_dirs()`](https://python-all.ru/3.4/library/profile.html#pstats.Stats.strip_dirs) method removed the extraneous path from all the module names. The [`sort_stats()`](https://python-all.ru/3.4/library/profile.html#pstats.Stats.sort_stats) method sorted all the entries according to the standard module/line/name string that is printed. The [`print_stats()`](https://python-all.ru/3.4/library/profile.html#pstats.Stats.print_stats) method printed out all the statistics. You might try the following sort calls:128129```python130p.sort_stats('name')131p.print_stats()132```133134Первый вызов сортирует список по имени функции, а второй вызов выводит статистику. Вот несколько интересных вызовов для экспериментов:135136```python137p.sort_stats('cumulative').print_stats(10)138```139140Это сортирует профиль по суммарному времени в функции и затем выводит только десять наиболее значимых строк. Чтобы понять, какие алгоритмы занимают время, следует использовать приведённую выше строку.141142Чтобы увидеть, какие функции много циклится и занимают много времени, можно сделать:143144```python145p.sort_stats('time').print_stats(10)146```147148для сортировки по времени, затраченному внутри каждой функции, и вывода статистики для десяти первых функций.149150Можно также попробовать:151152```python153p.sort_stats('file').print_stats('__init__')154```155156Это отсортирует всю статистику по имени файла, а затем выведет статистику только для методов инициализации классов (поскольку они содержат `__init__` в названии). В качестве последнего примера можно попробовать:157158```python159p.sort_stats('time', 'cumulative').print_stats(.5, 'init')160```161162Эта строка сортирует статистику по первичному ключу – времени, и вторичному ключу – совокупному времени, а затем выводит часть статистики. А именно: сначала список уменьшается до 50% (ср. `.5`) от исходного размера, затем сохраняются только строки, содержащие `init`, и выводится этот под-под-список.163164Если вам интересно, какие функции вызывали вышеупомянутые функции, теперь (`p` всё ещё отсортирован по последнему критерию) можно выполнить:165166```python167p.print_callers(.5, 'init')168```169170и будет получен список вызывающих для каждой из перечисленных функций.171172Для более широких возможностей придётся прочитать руководство или догадаться, что делают следующие функции:173174```python175p.print_callees()176p.add('restats')177```178179При запуске в качестве скрипта модуль [`pstats`](https://python-all.ru/3.4/library/profile.html#module-pstats) представляет собой браузер статистики для чтения и изучения дампов профилирования. Он имеет простой построчный интерфейс (реализованный с помощью [`cmd`](https://python-all.ru/3.4/library/cmd.html#module-cmd)) и интерактивную справку.180181## 27.4.3. Справочник модулей [`profile`](https://python-all.ru/3.4/library/profile.html#module-profile) и [`cProfile`](https://python-all.ru/3.4/library/profile.html#module-cProfile)182183Оба модуля [`profile`](https://python-all.ru/3.4/library/profile.html#module-profile) и [`cProfile`](https://python-all.ru/3.4/library/profile.html#module-cProfile) предоставляют следующие функции:184185#### `profile.run(command, filename=None, sort=-1)`186187Эта функция принимает единственный аргумент, который можно передать функции [`exec()`](https://python-all.ru/3.4/library/functions.html#exec), и необязательное имя файла. В любом случае эта процедура выполняет:188189```python190exec(command, __main__.__dict__, __main__.__dict__)191```192193и собирает статистику профилирования из выполнения. Если имя файла не указано, эта функция автоматически создает экземпляр [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats) и выводит простой отчет профилирования. Если указано значение sort, оно передается этому экземпляру [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats) для управления сортировкой результатов.194195#### `profile.runctx(command, globals, locals, filename=None)`196197Эта функция аналогична [`run()`](https://python-all.ru/3.4/library/profile.html#profile.run), с дополнительными аргументами для передачи словарей globals и locals для строки *command*. Эта процедура выполняет:198199```python200exec(command, globals, locals)201```202203и собирает статистику профилирования, как функция [`run()`](https://python-all.ru/3.4/library/profile.html#profile.run) выше.204205#### `class profile.Profile(timer=None, timeunit=0.0, subcalls=True, builtins=True)`206207Этот класс обычно используется только в тех случаях, когда требуется более точный контроль профилирования, чем предоставляет функция `cProfile.run()`.208209Можно указать собственный таймер для измерения времени выполнения кода с помощью аргумента *timer*. Это должна быть функция, возвращающая одно число, представляющее текущее время. Если число целое, *timeunit* задаёт множитель, определяющий длительность каждой единицы времени. Например, если таймер возвращает время, измеряемое в тысячных долях секунды, единица времени будет `.001`.210211Прямое использование класса [`Profile`](https://python-all.ru/3.4/library/profile.html#profile.Profile) позволяет форматировать результаты профилирования без записи данных профиля в файл:212213```python214import cProfile, pstats, io215pr = cProfile.Profile()216pr.enable()217# ... сделать что-то ...218pr.disable()219s = io.StringIO()220sortby = 'cumulative'221ps = pstats.Stats(pr, stream=s).sort_stats(sortby)222ps.print_stats()223print(s.getvalue())224```225226#### `enable()`227228Начинает сбор данных профилирования.229230#### `disable()`231232Прекращает сбор данных профилирования.233234#### `create_stats()`235236Остановить сбор данных профилирования и сохранить результаты внутри как текущий профиль.237238#### `print_stats(sort=-1)`239240Создает объект [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats) на основе текущего профиля и выводит результаты в stdout.241242#### `dump_stats(filename)`243244Записать результаты текущего профиля в *filename*.245246#### `run(cmd)`247248Профилирует cmd с помощью [`exec()`](https://python-all.ru/3.4/library/functions.html#exec).249250#### `runctx(cmd, globals, locals)`251252Профилирует cmd с помощью [`exec()`](https://python-all.ru/3.4/library/functions.html#exec) с указанным глобальным и локальным окружением.253254#### `runcall(func, *args, **kwargs)`255256Профилирует `func(*args, **kwargs)`257258## 27.4.4. Класс `Stats`259260Анализ данных профилировщика выполняется с помощью класса [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats).261262#### `class pstats.Stats(*filenames or profile, stream=sys.stdout)`263264Конструктор этого класса создает экземпляр объекта статистики из *filename* (или списка имен файлов) или из экземпляра `Profile`. Вывод будет направлен в поток данных, указанный параметром *stream*.265266Файл, выбранный указанным конструктором, должен быть создан соответствующей версией [`profile`](https://python-all.ru/3.4/library/profile.html#module-profile) или [`cProfile`](https://python-all.ru/3.4/library/profile.html#module-cProfile). Если конкретнее, то *не* гарантируется совместимость файлов с будущими версиями этого профилировщика, а также нет совместимости с файлами, созданными другими профилировщиками. Если предоставлено несколько файлов, вся статистика для одинаковых функций будет объединена, чтобы в одном отчете можно было рассмотреть общую картину нескольких процессов. Если необходимо объединить дополнительные файлы с данными существующего объекта [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats), можно использовать метод [`add()`](https://python-all.ru/3.4/library/profile.html#pstats.Stats.add).267268Вместо чтения данных профиля из файла в качестве источника данных профиля можно использовать объект `cProfile.Profile` или [`profile.Profile`](https://python-all.ru/3.4/library/profile.html#profile.Profile).269270Объекты [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats) имеют следующие методы:271272#### `strip_dirs()`273274Этот метод класса [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats) удаляет всю информацию о ведущих путях из имен файлов. Это очень полезно для уменьшения размера вывода, чтобы он помещался (почти) в 80 столбцов. Этот метод изменяет объект, и удаленная информация теряется. После операции удаления считается, что записи в объекте находятся в «случайном» порядке, как сразу после инициализации и загрузки объекта. Если [`strip_dirs()`](https://python-all.ru/3.4/library/profile.html#pstats.Stats.strip_dirs) приводит к тому, что два имени функций становятся неразличимыми (они находятся на одной строке одного файла и имеют одинаковое имя функции), то статистика для этих двух записей накапливается в одной записи.275276#### `add(*filenames)`277278Этот метод класса [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats) накапливает дополнительную информацию профилирования в текущем объекте профилирования. Его аргументы должны ссылаться на файлы, созданные соответствующей версией [`profile.run()`](https://python-all.ru/3.4/library/profile.html#profile.run) или `cProfile.run()`. Статистика для функций с одинаковыми именами (относительно: файл, строка, имя) автоматически накапливается в одну статистику функции.279280#### `dump_stats(filename)`281282Сохраняет данные, загруженные в объект [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats), в файл с именем *filename*. Файл создается, если он не существует, и перезаписывается, если он уже существует. Это эквивалентно одноименному методу классов [`profile.Profile`](https://python-all.ru/3.4/library/profile.html#profile.Profile) и `cProfile.Profile`.283284#### `sort_stats(*keys)`285286Этот метод изменяет объект [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats), сортируя его по заданным критериям. Аргументом обычно служит строка, определяющая основу сортировки (например: `'time'` или `'name'`).287288Если указано несколько ключей, то дополнительные используются как вторичные критерии, когда все предыдущие ключи равны. Например, `sort_stats('name', 'file')` отсортирует все записи по имени функции, а совпадения (одинаковые имена функций) разрешит сортировкой по имени файла.289290Сокращения могут использоваться для любых имён ключей, если сокращение недвусмысленно. Ниже перечислены текущие ключи:291292| Допустимый аргумент | Значение |293| --- | --- |294| `'calls'` | количество вызовов |295| `'cumulative'` | совокупное время |296| `'cumtime'` | совокупное время |297| `'file'` | имя файла |298| `'filename'` | имя файла |299| `'module'` | имя файла |300| `'ncalls'` | количество вызовов |301| `'pcalls'` | количество примитивных вызовов |302| `'line'` | номер строки |303| `'name'` | имя функции |304| `'nfl'` | имя/файл/строка |305| `'stdname'` | стандартное имя |306| `'time'` | внутреннее время |307| `'tottime'` | внутреннее время |308309Обратите внимание: все сортировки по статистике выполняются по убыванию (сначала самые затратные по времени элементы), тогда как поиск по имени, файлу и номеру строки – по возрастанию (в алфавитном порядке). Тонкое различие между `'nfl'` и `'stdname'` в том, что стандартное имя – это сортировка по напечатанному имени, а это означает, что встроенные номера строк сравниваются необычным образом. Например, строки 3, 20 и 40 (при одинаковых именах файлов) расположились бы в строковом порядке как 20, 3, 40. В отличие от этого, `'nfl'` выполняет численное сравнение номеров строк. Фактически, `sort_stats('nfl')` – это то же самое, что `sort_stats('name', 'file', 'line')`.310311По соображениям обратной совместимости допускаются числовые аргументы `-1`, `0`, `1` и `2`. Они интерпретируются соответственно как `'stdname'`, `'calls'`, `'time'` и `'cumulative'`. Если используется этот старый формат (числовой), то будет применён только один ключ сортировки (числовой), а остальные аргументы будут молча проигнорированы.312313#### `reverse_order()`314315Этот метод класса [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats) меняет порядок базового списка внутри объекта на обратный. Учтите, что по умолчанию порядок по возрастанию или убыванию выбирается правильно в зависимости от выбранного ключа сортировки.316317#### `print_stats(*restrictions)`318319Этот метод класса [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats) выводит отчёт, как описано в определении [`profile.run()`](https://python-all.ru/3.4/library/profile.html#profile.run).320321Порядок вывода основан на последней операции [`sort_stats()`](https://python-all.ru/3.4/library/profile.html#pstats.Stats.sort_stats), выполненной над объектом (с учётом оговорок в [`add()`](https://python-all.ru/3.4/library/profile.html#pstats.Stats.add) и [`strip_dirs()`](https://python-all.ru/3.4/library/profile.html#pstats.Stats.strip_dirs)).322323Переданные аргументы (если есть) позволяют ограничить список значимыми записями. Изначально список включает все профилированные функции. Каждое ограничение – это либо целое число (чтобы выбрать количество строк), либо десятичная дробь от 0.0 до 1.0 включительно (чтобы выбрать процент строк), либо регулярное выражение (для сопоставления с выводимым стандартным именем). Если указано несколько ограничений, они применяются последовательно. Например:324325```python326print_stats(.1, 'foo:')327```328329сначала ограничит вывод первыми 10% списка, а затем выведет только те функции, которые входят в имя файла `.*foo:`. В отличие от этого, команда:330331```python332print_stats('foo:', .1)333```334335ограничит список всеми функциями с именами файлов `.*foo:`, а затем выведет только первые 10% из них.336337#### `print_callers(*restrictions)`338339Этот метод класса [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats) выводит список всех функций, которые вызывали каждую функцию из профилированной базы данных. Порядок совпадает с [`print_stats()`](https://python-all.ru/3.4/library/profile.html#pstats.Stats.print_stats), и определение аргумента-ограничения также совпадает. Каждый вызывающий выводится на отдельной строке. Формат немного отличается в зависимости от профилировщика, создавшего статистику:340341- Для [`profile`](https://python-all.ru/3.4/library/profile.html#module-profile) после каждого вызывающего в скобках показывается число, указывающее, сколько раз был сделан этот конкретный вызов. Для удобства второе число (без скобок) справа повторяет совокупное время, затраченное в функции.342- Для [`cProfile`](https://python-all.ru/3.4/library/profile.html#module-cProfile) перед каждым вызывающим указывается три числа: сколько раз был сделан этот конкретный вызов, а также общее и совокупное время, затраченное в текущей функции при её вызове из этого конкретного вызывающего.343344#### `print_callees(*restrictions)`345346Этот метод для класса [`Stats`](https://python-all.ru/3.4/library/profile.html#pstats.Stats) выводит список всех функций, которые были вызваны указанной функцией. За исключением этого изменения направления вызовов (т.е. «вызванные» вместо «вызываемые»), аргументы и порядок сортировки идентичны методу [`print_callers()`](https://python-all.ru/3.4/library/profile.html#pstats.Stats.print_callers).347348## 27.4.5. Что такое детерминированное профилирование?349350*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 not done by this module) randomly 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.351352В Python, поскольку во время выполнения активен интерпретатор, для детерминированного профилирования не требуется наличие инструментированного кода. Python автоматически предоставляет *ловушку* (необязательный колбэк) для каждого события. Кроме того, интерпретируемая природа Python обычно вносит так много накладных расходов на выполнение, что детерминированное профилирование в типичных приложениях добавляет лишь небольшие дополнительные накладные расходы. В результате детерминированное профилирование не так дорого, но при этом предоставляет обширную статистику времени выполнения программы на Python.353354Статистику количества вызовов можно использовать для выявления ошибок в коде (неожиданные количества) и для определения возможных точек встраивания (высокое количество вызовов). Статистику внутреннего времени можно использовать для выявления «горячих циклов», которые следует тщательно оптимизировать. Статистику суммарного времени следует использовать для выявления высокоуровневых ошибок в выборе алгоритмов. Обратите внимание, что нестандартная обработка суммарного времени в этом профилировщике позволяет напрямую сравнивать статистику рекурсивных реализаций алгоритмов с итеративными.355356## 27.4.6. Ограничения357358Одно ограничение связано с точностью информации о времени. Существует фундаментальная проблема с детерминированными профилировщиками, касающаяся точности. Наиболее очевидное ограничение состоит в том, что внутренние «часы» тикают с частотой (обычно) около 0,001 секунды. Следовательно, ни одно измерение не может быть точнее этих часов. Если сделать достаточно измерений, «ошибка» будет стремиться к усреднению. К сожалению, устранение этой первой ошибки приводит ко второму источнику ошибок.359360The second problem is that it “takes a while” from when an event is dispatched until the profiler’s call to get the time actually *gets* the state of the clock. Similarly, there is a certain lag when exiting the profiler event handler from the time that the clock’s value was obtained (and then squirreled away), until the user’s code is once again executing. As a result, functions that are called many times, or call many functions, will typically accumulate this error. The error that accumulates in this fashion is typically less than the accuracy of the clock (less than one clock tick), but it *can* accumulate and become very significant.361362Проблема более значима для [`profile`](https://python-all.ru/3.4/library/profile.html#module-profile), чем для менее нагруженного [`cProfile`](https://python-all.ru/3.4/library/profile.html#module-cProfile). По этой причине [`profile`](https://python-all.ru/3.4/library/profile.html#module-profile) предоставляет средство калибровки под конкретную платформу, чтобы эту погрешность можно было вероятностно (в среднем) устранить. После калибровки профилировщик станет точнее (в смысле наименьших квадратов), но иногда будет выдавать отрицательные числа (когда количество вызовов исключительно мало, и боги вероятности играют против вас :-). *Не* стоит пугаться отрицательных чисел в профиле. Они должны *только* появляться, если вы откалибровали профилировщик, и результаты на самом деле лучше, чем без калибровки.363364## 27.4.7. Калибровка365366Профилировщик модуля [`profile`](https://python-all.ru/3.4/library/profile.html#module-profile) вычитает константу из времени обработки каждого события, чтобы скомпенсировать накладные расходы на вызов функции времени и сохранение результатов. По умолчанию константа равна 0. Следующая процедура позволяет получить более точную константу для конкретной платформы (см. [*Limitations*](https://python-all.ru/3.4/library/profile.html#profile-limitations)).367368```python369import profile370pr = profile.Profile()371for i in range(5):372 print(pr.calibrate(10000))373```374375Метод выполняет указанное аргументом количество вызовов Python напрямую и снова под профилировщиком, измеряя время для обоих случаев. Затем он вычисляет скрытые накладные расходы на каждое событие профилировщика и возвращает их в виде числа с плавающей точкой. Например, на 1.8 ГГц Intel Core i5 под управлением Mac OS X с использованием time.clock() в качестве таймера магическое число составляет около 4.04e-6.376377Цель этого упражнения – получить достаточно стабильный результат. Если ваш компьютер *очень* быстрый или функция таймера имеет низкое разрешение, возможно, придётся передать 100000 или даже 1000000, чтобы получить стабильные результаты.378379Когда получен стабильный результат, его можно использовать тремя способами:380381```python382import profile383384# 1. Применить вычисленное смещение ко всем экземплярам Profile, создаваемым в дальнейшем.385profile.Profile.bias = your_computed_bias386387# 2. Применить вычисленное смещение к конкретному экземпляру Profile.388pr = profile.Profile()389pr.bias = your_computed_bias390391# 3. Указать вычисленное смещение в конструкторе экземпляра.392pr = profile.Profile(bias=your_computed_bias)393```394395Если есть выбор, лучше выбрать меньшую константу – тогда результаты будут «реже» отображаться как отрицательные в статистике профилирования.396397## 27.4.8. Использование пользовательского таймера398399Если требуется изменить способ определения текущего времени (например, принудительно использовать настенное время или прошедшее процессорное время), передайте нужную функцию замера времени конструктору класса `Profile`:400401```python402pr = profile.Profile(your_time_func)403```404405Полученный профилировщик будет вызывать `your_time_func`. В зависимости от того, используется [`profile.Profile`](https://python-all.ru/3.4/library/profile.html#profile.Profile) или `cProfile.Profile`, возвращаемое значение `your_time_func` будет интерпретироваться по-разному:406407**[`profile.Profile`](https://python-all.ru/3.4/library/profile.html#profile.Profile)**408409`your_time_func` должна возвращать одно число или список чисел, сумма которых равна текущему времени (как возвращает [`os.times()`](https://python-all.ru/3.4/library/os.html#os.times)). Если функция возвращает одно число или список длиной 2, то будет использована особенно быстрая версия диспетчерской подпрограммы.410411Следует иметь в виду, что перед использованием нужно откалибровать класс профилировщика под выбранную функцию замера времени (см. [*Calibration*](https://python-all.ru/3.4/library/profile.html#profile-calibration)). Для большинства машин таймер, возвращающий одно целое число, даёт наилучшие результаты с точки зрения низких накладных расходов при профилировании. ([`os.times()`](https://python-all.ru/3.4/library/os.html#os.times) *достаточно* плох, так как возвращает кортеж чисел с плавающей запятой). Если требуется заменить таймер наиболее чистым способом, создайте производный класс и жёстко пропишите заменяющий метод диспетчеризации, который наилучшим образом обрабатывает вызов вашего таймера, вместе с подходящей константой калибровки.412413**`cProfile.Profile`**414415`your_time_func` должна возвращать одно число. Если она возвращает целые числа, можно также вызвать конструктор класса со вторым аргументом, задающим реальную длительность одной единицы времени. Например, если `your_integer_time_func` возвращает время, измеренное в тысячных долях секунды, экземпляр `Profile` создаётся следующим образом:416417```python418pr = cProfile.Profile(your_integer_time_func, 0.001)419```420421Поскольку класс `cProfile.Profile` невозможно откалибровать, пользовательские функции замера времени следует использовать с осторожностью, и они должны быть максимально быстрыми. Для наилучших результатов с пользовательским таймером может потребоваться жёстко прописать его в C-исходниках внутреннего модуля `_lsprof`.422423В Python 3.3 добавлено несколько новых функций в модуль [`time`](https://python-all.ru/3.4/library/time.html#module-time), которые можно использовать для точных измерений процессорного или настенного времени. Например, см. [`time.perf_counter()`](https://python-all.ru/3.4/library/time.html#time.perf_counter).424