profile.md
1> **Источник:** https://python-all.ru/2.7/library/profile.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 26.4. Профилировщики Python89**Исходный код:** [Lib/profile.py](https://python-all.ru/src/2.7/Lib/profile.py) и [Lib/pstats.py](https://python-all.ru/src/2.7/Lib/pstats.py)1011---1213## 26.4.1. Введение в профилировщики1415[`cProfile`](https://python-all.ru/2.7/library/profile.html#module-cProfile) и [`profile`](https://python-all.ru/2.7/library/profile.html#module-profile) предоставляют *детерминированное профилирование* программ Python. *Профиль* – это набор статистики, описывающий, как часто и как долго выполнялись различные части программы. Эти статистические данные могут быть отформатированы в отчёты с помощью модуля [`pstats`](https://python-all.ru/2.7/library/profile.html#module-pstats).1617Стандартная библиотека Python предоставляет три разные реализации одного и того же интерфейса профилирования:18191. [`cProfile`](https://python-all.ru/2.7/library/profile.html#module-cProfile) рекомендуется для большинства пользователей; это расширение на C с приемлемыми накладными расходами, что делает его подходящим для профилирования длительно работающих программ. Основан на `lsprof`, предоставленном Бреттом Розеном и Тедом Чоттером.2021 Новое в версии 2.5.222. [`profile`](https://python-all.ru/2.7/library/profile.html#module-profile), чисто Python-модуль, чей интерфейс имитируется [`cProfile`](https://python-all.ru/2.7/library/profile.html#module-cProfile), но который добавляет значительные накладные расходы профилируемым программам. Если требуется расширить профилировщик каким-либо образом, задача может оказаться проще с этим модулем. Изначально спроектирован и написан Джимом Роскиндом.2324 Изменено в версии 2.4: Теперь также сообщается время, затраченное на вызовы встроенных функций и методов.253. [`hotshot`](https://python-all.ru/2.7/library/hotshot.html#module-hotshot) был экспериментальным модулем на C, ориентированным на минимизацию накладных расходов профилирования ценой увеличения времени последующей обработки данных. Он больше не поддерживается и может быть удалён в одной из будущих версий Python.2627 Изменено в версии 2.5: Результаты теперь должны быть более осмысленными, чем раньше: ядро измерения времени содержало критическую ошибку.2829Модули [`profile`](https://python-all.ru/2.7/library/profile.html#module-profile) и [`cProfile`](https://python-all.ru/2.7/library/profile.html#module-cProfile) экспортируют один и тот же интерфейс, так что они в основном взаимозаменяемы; [`cProfile`](https://python-all.ru/2.7/library/profile.html#module-cProfile) имеет гораздо меньшие накладные расходы, но является более новым и может быть недоступен во всех системах. [`cProfile`](https://python-all.ru/2.7/library/profile.html#module-cProfile) на самом деле является уровнем совместимости поверх внутреннего модуля `_lsprof`. Модуль [`hotshot`](https://python-all.ru/2.7/library/hotshot.html#module-hotshot) предназначен для специализированного использования.3031> **Примечание**32>33> Модули профилирования предназначены для получения профиля выполнения данной программы, а не для целей бенчмаркинга (для этого существует [`timeit`](https://python-all.ru/2.7/library/timeit.html#module-timeit) для достаточно точных результатов). Это особенно относится к сравнению производительности Python-кода с C-кодом: профилировщики добавляют накладные расходы для Python-кода, но не для C-функций, поэтому C-код будет казаться быстрее любого Python-кода.3435## 26.4.2. Краткое руководство пользователя3637Этот раздел предназначен для пользователей, которые «не хотят читать руководство». В нём даётся очень краткий обзор, и он позволяет быстро выполнить профилирование существующего приложения.3839Чтобы профилировать функцию, принимающую один аргумент, можно сделать следующее:4041```python42import cProfile43import re44cProfile.run('re.compile("foo|bar")')45```4647(Use [`profile`](https://python-all.ru/2.7/library/profile.html#module-profile) instead of [`cProfile`](https://python-all.ru/2.7/library/profile.html#module-cProfile) if the latter is not available on your system.)4849Приведённое выше действие запустит [`re.compile()`](https://python-all.ru/2.7/library/re.html#re.compile) и выведет результаты профилирования, как показано ниже:5051```python52 197 function calls (192 primitive calls) in 0.002 seconds5354Ordered by: standard name5556ncalls tottime percall cumtime percall filename:lineno(function)57 1 0.000 0.000 0.001 0.001 <string>:1(<module>)58 1 0.000 0.000 0.001 0.001 re.py:212(compile)59 1 0.000 0.000 0.001 0.001 re.py:268(_compile)60 1 0.000 0.000 0.000 0.000 sre_compile.py:172(_compile_charset)61 1 0.000 0.000 0.000 0.000 sre_compile.py:201(_optimize_charset)62 4 0.000 0.000 0.000 0.000 sre_compile.py:25(_identityfunction)63 3/1 0.000 0.000 0.000 0.000 sre_compile.py:33(_compile)64```6566Первая строка показывает, что было отслежено 197 вызовов. Из них 192 были *примитивными*, то есть не вызваны рекурсией. Следующая строка: `Ordered by: standard name`, указывает, что текстовая строка в крайнем правом столбце использовалась для сортировки вывода. Заголовки столбцов включают:6768**ncalls**6970– количество вызовов,7172**tottime**7374– общее время, затраченное в данной функции (исключая время, затраченное в вызовах подфункций).7576**percall**7778– частное от деления `tottime` на `ncalls`7980**cumtime**8182– совокупное время, затраченное в этой и всех подфункциях (от вызова до выхода). Это значение точное *даже* для рекурсивных функций.8384**percall**8586– частное от деления `cumtime` на количество примитивных вызовов8788**filename:lineno(function)**8990– предоставляет соответствующие данные каждой функции9192Когда в первом столбце два числа (например, `3/1`), это означает, что функция выполняла рекурсию. Второе значение – количество примитивных вызовов, а первое – общее количество вызовов. Обратите внимание: если функция не рекурсивна, эти два значения совпадают, и печатается только одно число.9394Вместо вывода результатов в конце сеанса профилирования можно сохранить результаты в файл, указав имя файла функции `run()`:9596```python97import cProfile98import re99cProfile.run('re.compile("foo|bar")', 'restats')100```101102Класс [`pstats.Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats) читает результаты профилирования из файла и форматирует их различными способами.103104Файл [`cProfile`](https://python-all.ru/2.7/library/profile.html#module-cProfile) можно также запустить как сценарий для профилирования другого сценария. Например:105106```python107python -m cProfile [-o output_file] [-s sort_order] myscript.py108```109110`-o` записывает результаты профилирования в файл, а не в stdout111112`-s` задаёт одно из значений сортировки [`sort_stats()`](https://python-all.ru/2.7/library/profile.html#pstats.Stats.sort_stats) для сортировки вывода. Применяется, только если `-o` не указан.113114Класс [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats) модуля [`pstats`](https://python-all.ru/2.7/library/profile.html#module-pstats) имеет различные методы для обработки и вывода данных, сохранённых в файл результатов профилирования:115116```python117import pstats118p = pstats.Stats('restats')119p.strip_dirs().sort_stats(-1).print_stats()120```121122Метод [`strip_dirs()`](https://python-all.ru/2.7/library/profile.html#pstats.Stats.strip_dirs) удаляет лишние пути из всех имён модулей. Метод [`sort_stats()`](https://python-all.ru/2.7/library/profile.html#pstats.Stats.sort_stats) сортирует все записи по стандартной строке модуль/строка/имя, которая выводится. Метод [`print_stats()`](https://python-all.ru/2.7/library/profile.html#pstats.Stats.print_stats) выводит всю статистику. Можно попробовать следующие вызовы сортировки:123124```python125p.sort_stats('name')126p.print_stats()127```128129Первый вызов сортирует список по имени функции, а второй вызов выводит статистику. Вот несколько интересных вызовов для экспериментов:130131```python132p.sort_stats('cumulative').print_stats(10)133```134135Это сортирует профиль по суммарному времени в функции и затем выводит только десять наиболее значимых строк. Чтобы понять, какие алгоритмы занимают время, следует использовать приведённую выше строку.136137Чтобы увидеть, какие функции много циклится и занимают много времени, можно сделать:138139```python140p.sort_stats('time').print_stats(10)141```142143для сортировки по времени, затраченному внутри каждой функции, и вывода статистики для десяти первых функций.144145Можно также попробовать:146147```python148p.sort_stats('file').print_stats('__init__')149```150151Это отсортирует всю статистику по имени файла и затем выведет статистику только для методов инициализации классов (поскольку они содержат `__init__` в названии). В качестве последнего примера можно попробовать:152153```python154p.sort_stats('time', 'cum').print_stats(.5, 'init')155```156157Эта строка сортирует статистику сначала по времени, затем по суммарному времени, и выводит часть статистики. Если точнее, список сначала сокращается до 50% (отн. `.5`) от исходного размера, затем оставляются только строки, содержащие `init`, и выводится этот под-под-список.158159Если интересно, какие функции вызывали вышеуказанные функции, теперь (`p` всё ещё отсортирован по последнему критерию) можно сделать:160161```python162p.print_callers(.5, 'init')163```164165и будет получен список вызывающих для каждой из перечисленных функций.166167Для более широких возможностей придётся прочитать руководство или догадаться, что делают следующие функции:168169```python170p.print_callees()171p.add('restats')172```173174При запуске как скрипт, модуль [`pstats`](https://python-all.ru/2.7/library/profile.html#module-pstats) является браузером статистики для чтения и просмотра дампов профилирования. Он имеет простой построчный интерфейс (реализованный с помощью [`cmd`](https://python-all.ru/2.7/library/cmd.html#module-cmd)) и интерактивную справку.175176## 26.4.3. [`profile`](https://python-all.ru/2.7/library/profile.html#module-profile) и [`cProfile`](https://python-all.ru/2.7/library/profile.html#module-cProfile) Справочник по модулю177178Оба модуля [`profile`](https://python-all.ru/2.7/library/profile.html#module-profile) и [`cProfile`](https://python-all.ru/2.7/library/profile.html#module-cProfile) предоставляют следующие функции:179180#### `profile.run(command, filename=None, sort=-1)`181182Эта функция принимает один аргумент, который можно передать функции `exec()`, и необязательное имя файла. Во всех случаях эта процедура выполняет:183184```python185exec(command, __main__.__dict__, __main__.__dict__)186```187188и собирает статистику профилирования из выполнения. Если имя файла не указано, эта функция автоматически создаёт экземпляр [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats) и выводит простой отчёт профилирования. Если указано значение sort, оно передаётся этому экземпляру [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats) для управления сортировкой результатов.189190#### `profile.runctx(command, globals, locals, filename=None)`191192Эта функция аналогична [`run()`](https://python-all.ru/2.7/library/profile.html#profile.run), но с дополнительными аргументами для передачи словарей globals и locals для строки *command*. Данная процедура выполняет:193194```python195exec(command, globals, locals)196```197198и собирает статистику профилирования, как в функции [`run()`](https://python-all.ru/2.7/library/profile.html#profile.run) выше.199200#### `class profile.Profile(timer=None, timeunit=0.0, subcalls=True, builtins=True)`201202Этот класс обычно используется только тогда, когда требуется более точный контроль профилирования, чем предоставляет функция `cProfile.run()`.203204Можно указать собственный таймер для измерения времени выполнения кода с помощью аргумента *timer*. Это должна быть функция, возвращающая одно число, представляющее текущее время. Если число целое, *timeunit* задаёт множитель, определяющий длительность каждой единицы времени. Например, если таймер возвращает время, измеряемое в тысячных долях секунды, единица времени будет `.001`.205206Непосредственное использование класса [`Profile`](https://python-all.ru/2.7/library/profile.html#profile.Profile) позволяет форматировать результаты профилирования без записи данных профиля в файл:207208```python209import cProfile, pstats, StringIO210pr = cProfile.Profile()211pr.enable()212# ... сделать что-то ...213pr.disable()214s = StringIO.StringIO()215sortby = 'cumulative'216ps = pstats.Stats(pr, stream=s).sort_stats(sortby)217ps.print_stats()218print s.getvalue()219```220221#### `enable()`222223Начинает сбор данных профилирования.224225#### `disable()`226227Прекращает сбор данных профилирования.228229#### `create_stats()`230231Остановить сбор данных профилирования и сохранить результаты внутри как текущий профиль.232233#### `print_stats(sort=-1)`234235Создать объект [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats) на основе текущего профиля и вывести результаты в stdout.236237#### `dump_stats(filename)`238239Записать результаты текущего профиля в *filename*.240241#### `run(cmd)`242243Профилировать cmd с помощью `exec()`.244245#### `runctx(cmd, globals, locals)`246247Профилировать cmd с помощью `exec()` с указанным глобальным и локальным окружением.248249#### `runcall(func, *args, **kwargs)`250251Профилировать `func(*args, **kwargs)`252253## 26.4.4. Класс `Stats`254255Анализ данных профилировщика выполняется с помощью класса [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats).256257#### `class pstats.Stats(*filenames or profile, stream=sys.stdout)`258259Конструктор этого класса создаёт экземпляр «объекта статистики» из *filename* (или списка имён файлов) или из экземпляра `Profile`. Вывод будет направлен в поток данных, указанный параметром *stream*.260261Файл, указанный в конструкторе, должен быть создан соответствующей версией [`profile`](https://python-all.ru/2.7/library/profile.html#module-profile) или [`cProfile`](https://python-all.ru/2.7/library/profile.html#module-cProfile). Если точнее, *нет* гарантии совместимости файлов с будущими версиями этого профилировщика, а также нет совместимости с файлами, созданными другими профилировщиками или тем же профилировщиком в другой операционной системе. Если передано несколько файлов, вся статистика для одинаковых функций объединяется, чтобы в одном отчёте можно было получить общую картину по нескольким процессам. Если необходимо объединить дополнительные файлы с данными существующего объекта [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats), можно использовать метод [`add()`](https://python-all.ru/2.7/library/profile.html#pstats.Stats.add).262263Вместо чтения данных профиля из файла можно использовать объект `cProfile.Profile` или [`profile.Profile`](https://python-all.ru/2.7/library/profile.html#profile.Profile) в качестве источника данных профиля.264265Объекты [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats) имеют следующие методы:266267#### `strip_dirs()`268269Этот метод класса [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats) удаляет всю информацию о начальных путях из имён файлов. Он очень полезен для уменьшения размера вывода, чтобы он помещался (примерно) в 80 столбцов. Этот метод изменяет объект, и удалённая информация теряется. После выполнения операции удаления пути записи объекта считаются находящимися в «случайном» порядке, как сразу после инициализации и загрузки объекта. Если [`strip_dirs()`](https://python-all.ru/2.7/library/profile.html#pstats.Stats.strip_dirs) приводит к тому, что два имени функций становятся неразличимыми (они находятся на одной строке одного и того же файла и имеют одинаковое имя функции), то статистика для этих двух записей объединяется в одну запись.270271#### `add(*filenames)`272273Этот метод класса [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats) накапливает дополнительную информацию профилирования в текущем объекте профилирования. Его аргументы должны указывать на имена файлов, созданные соответствующей версией [`profile.run()`](https://python-all.ru/2.7/library/profile.html#profile.run) или `cProfile.run()`. Статистика для функций с одинаковыми именами (файл, строка, имя) автоматически накапливается в единую статистику функции.274275#### `dump_stats(filename)`276277Сохраняет данные, загруженные в объект [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats), в файл с именем *filename*. Файл создаётся, если его не существует, и перезаписывается, если он уже существует. Это эквивалентно одноимённому методу классов [`profile.Profile`](https://python-all.ru/2.7/library/profile.html#profile.Profile) и `cProfile.Profile`.278279Новое в версии 2.3.280281#### `sort_stats(*keys)`282283Этот метод модифицирует объект [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats), сортируя его в соответствии с заданными критериями. Аргумент обычно является строкой, определяющей основу сортировки (пример: `'time'` или `'name'`).284285Если указано более одного ключа, то дополнительные ключи используются как вторичные критерии, когда все ранее выбранные ключи равны. Например, `sort_stats('name', 'file')` отсортирует все записи по имени функции и разрешит все совпадения (одинаковые имена функций) сортировкой по имени файла.286287Сокращения могут использоваться для любых имён ключей, если сокращение недвусмысленно. Ниже перечислены текущие ключи:288289| Допустимый аргумент | Значение |290| --- | --- |291| `'calls'` | количество вызовов |292| `'cumulative'` | совокупное время |293| `'cumtime'` | совокупное время |294| `'file'` | имя файла |295| `'filename'` | имя файла |296| `'module'` | имя файла |297| `'ncalls'` | количество вызовов |298| `'pcalls'` | количество примитивных вызовов |299| `'line'` | номер строки |300| `'name'` | имя функции |301| `'nfl'` | имя/файл/строка |302| `'stdname'` | стандартное имя |303| `'time'` | внутреннее время |304| `'tottime'` | внутреннее время |305306Обратите внимание, что все сортировки по статистике выполняются по убыванию (сначала наиболее затратные по времени элементы), тогда как поиск по имени, файлу и номеру строки выполняется по возрастанию (в алфавитном порядке). Тонкое различие между `'nfl'` и `'stdname'` заключается в том, что стандартное имя – это сортировка по выводимому имени, что означает, что встроенные номера строк сравниваются необычным образом. Например, строки 3, 20 и 40 будут (если имена файлов были одинаковы) отображаться в строковом порядке 20, 3 и 40. В отличие от этого, `'nfl'` выполняет числовое сравнение номеров строк. Фактически, `sort_stats('nfl')` эквивалентно `sort_stats('name', 'file', 'line')`.307308По причинам обратной совместимости допускаются числовые аргументы `-1`, `0`, `1` и `2`. Они интерпретируются как `'stdname'`, `'calls'`, `'time'` и `'cumulative'` соответственно. Если используется старый формат (числовой), будет использоваться только один ключ сортировки (числовой), а дополнительные аргументы будут молча игнорироваться.309310#### `reverse_order()`311312Этот метод для класса [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats) меняет порядок основного списка внутри объекта. Обратите внимание, что по умолчанию порядок (по возрастанию или убыванию) выбирается правильно на основе выбранного ключа сортировки.313314#### `print_stats(*restrictions)`315316Этот метод для класса [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats) выводит отчёт, как описано в определении [`profile.run()`](https://python-all.ru/2.7/library/profile.html#profile.run).317318Порядок вывода основан на последней операции [`sort_stats()`](https://python-all.ru/2.7/library/profile.html#pstats.Stats.sort_stats), выполненной над объектом (с учётом оговорок в [`add()`](https://python-all.ru/2.7/library/profile.html#pstats.Stats.add) и [`strip_dirs()`](https://python-all.ru/2.7/library/profile.html#pstats.Stats.strip_dirs)).319320Переданные аргументы (если есть) позволяют ограничить список значимыми записями. Изначально список включает все профилированные функции. Каждое ограничение – это либо целое число (чтобы выбрать количество строк), либо десятичная дробь от 0.0 до 1.0 включительно (чтобы выбрать процент строк), либо регулярное выражение (для сопоставления с выводимым стандартным именем). Если указано несколько ограничений, они применяются последовательно. Например:321322```python323print_stats(.1, 'foo:')324```325326сначала ограничит вывод первыми 10% списка, а затем напечатает только функции, относящиеся к файлу с именем `.*foo:`. В отличие от этого, команда:327328```python329print_stats('foo:', .1)330```331332ограничит список всеми функциями, имеющими имена файлов `.*foo:`, а затем выведет только первые 10% из них.333334#### `print_callers(*restrictions)`335336Этот метод для класса [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats) выводит список всех функций, которые вызывали каждую функцию в профилированной базе данных. Порядок такой же, как у [`print_stats()`](https://python-all.ru/2.7/library/profile.html#pstats.Stats.print_stats), и определение аргумента-ограничения тоже идентично. Каждая вызывающая функция выводится на отдельной строке. Формат немного различается в зависимости от профилировщика, который создал статистику:337338- С [`profile`](https://python-all.ru/2.7/library/profile.html#module-profile) после каждой вызывающей функции в скобках указывается число, показывающее, сколько раз был выполнен этот конкретный вызов. Для удобства второе число без скобок повторяет суммарное время, затраченное в функции, справа.339- С [`cProfile`](https://python-all.ru/2.7/library/profile.html#module-cProfile) перед каждой вызывающей функцией указываются три числа: количество раз, когда был выполнен этот конкретный вызов, а также общее и суммарное время, затраченное в текущей функции при её вызове данной вызывающей функцией.340341#### `print_callees(*restrictions)`342343Этот метод для класса [`Stats`](https://python-all.ru/2.7/library/profile.html#pstats.Stats) выводит список всех функций, которые были вызваны указанной функцией. За исключением этого изменения направления вызовов (вызывала vs была вызвана), аргументы и порядок идентичны методу [`print_callers()`](https://python-all.ru/2.7/library/profile.html#pstats.Stats.print_callers).344345## 26.4.5. Что такое детерминированное профилирование?346347*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.348349В Python, поскольку во время выполнения активен интерпретатор, для детерминированного профилирования не требуется наличие инструментированного кода. Python автоматически предоставляет *ловушку* (необязательный колбэк) для каждого события. Кроме того, интерпретируемая природа Python обычно вносит так много накладных расходов на выполнение, что детерминированное профилирование в типичных приложениях добавляет лишь небольшие дополнительные накладные расходы. В результате детерминированное профилирование не так дорого, но при этом предоставляет обширную статистику времени выполнения программы на Python.350351Статистику количества вызовов можно использовать для выявления ошибок в коде (неожиданные количества) и для определения возможных точек встраивания (высокое количество вызовов). Статистику внутреннего времени можно использовать для выявления «горячих циклов», которые следует тщательно оптимизировать. Статистику суммарного времени следует использовать для выявления высокоуровневых ошибок в выборе алгоритмов. Обратите внимание, что нестандартная обработка суммарного времени в этом профилировщике позволяет напрямую сравнивать статистику рекурсивных реализаций алгоритмов с итеративными.352353## 26.4.6. Ограничения354355Одно ограничение связано с точностью информации о времени. Существует фундаментальная проблема с детерминированными профилировщиками, касающаяся точности. Наиболее очевидное ограничение состоит в том, что внутренние «часы» тикают с частотой (обычно) около 0,001 секунды. Следовательно, ни одно измерение не может быть точнее этих часов. Если сделать достаточно измерений, «ошибка» будет стремиться к усреднению. К сожалению, устранение этой первой ошибки приводит ко второму источнику ошибок.356357The 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.358359Эта проблема более важна для [`profile`](https://python-all.ru/2.7/library/profile.html#module-profile), чем для [`cProfile`](https://python-all.ru/2.7/library/profile.html#module-cProfile) с меньшими накладными расходами. По этой причине [`profile`](https://python-all.ru/2.7/library/profile.html#module-profile) предоставляет возможность калибровки для заданной платформы, чтобы эту ошибку можно было вероятностно (в среднем) устранить. После калибровки профилировщика он становится точнее (в смысле наименьших квадратов), но иногда выдаёт отрицательные числа (когда количество вызовов исключительно мало, и боги вероятности против вас :-)). Не *стоит пугаться* отрицательных чисел в профиле. Они должны *появляться только*, если вы откалибровали профилировщик, и результаты на самом деле лучше, чем без калибровки.360361## 26.4.7. Калибровка362363Профилировщик модуля [`profile`](https://python-all.ru/2.7/library/profile.html#module-profile) вычитает константу из времени обработки каждого события, чтобы компенсировать накладные расходы на вызов функции времени и сохранение результатов. По умолчанию константа равна 0. Следующую процедуру можно использовать для получения лучшей константы для данной платформы (см. [Ограничения](https://python-all.ru/2.7/library/profile.html#profile-limitations)).364365```python366import profile367pr = profile.Profile()368for i in range(5):369 print pr.calibrate(10000)370```371372Метод выполняет указанное аргументом количество вызовов Python напрямую и снова под профилировщиком, измеряя время для обоих случаев. Затем он вычисляет скрытые накладные расходы на каждое событие профилировщика и возвращает их в виде числа с плавающей точкой. Например, на 1.8 ГГц Intel Core i5 под управлением Mac OS X с использованием time.clock() в качестве таймера магическое число составляет около 4.04e-6.373374Цель этого упражнения – получить достаточно стабильный результат. Если ваш компьютер *очень* быстрый или функция таймера имеет низкое разрешение, возможно, придётся передать 100000 или даже 1000000, чтобы получить стабильные результаты.375376Если получен согласованный ответ, есть три способа его использования: [1](https://python-all.ru/2.7/library/profile.html#id2)377378```python379import profile380381# 1. Применить вычисленное смещение ко всем экземплярам Profile, создаваемым в дальнейшем.382profile.Profile.bias = your_computed_bias383384# 2. Применить вычисленное смещение к конкретному экземпляру Profile.385pr = profile.Profile()386pr.bias = your_computed_bias387388# 3. Указать вычисленное смещение в конструкторе экземпляра.389pr = profile.Profile(bias=your_computed_bias)390```391392Если есть выбор, лучше выбрать меньшую константу – тогда результаты будут «реже» отображаться как отрицательные в статистике профилирования.393394## 26.4.8. Использование пользовательского таймера395396Если требуется изменить способ определения текущего времени (например, принудительно использовать wall-clock time или время процесса), передайте нужную функцию таймера в конструктор класса `Profile`:397398```python399pr = profile.Profile(your_time_func)400```401402Полученный профилировщик будет вызывать `your_time_func`. В зависимости от того, используется ли [`profile.Profile`](https://python-all.ru/2.7/library/profile.html#profile.Profile) или `cProfile.Profile`, возвращаемое значение `your_time_func` будет интерпретироваться по-разному:403404**[`profile.Profile`](https://python-all.ru/2.7/library/profile.html#profile.Profile)**405406`your_time_func` должна возвращать одно число или список чисел, сумма которых равна текущему времени (как [`os.times()`](https://python-all.ru/2.7/library/os.html#os.times)). Если функция возвращает одно число времени или длина возвращаемого списка равна 2, то будет использована особенно быстрая версия диспетчерской подпрограммы.407408Следует учесть, что необходимо откалибровать класс профилировщика для выбранной функции таймера (см. [Калибровка](https://python-all.ru/2.7/library/profile.html#profile-calibration)). Для большинства машин таймер, возвращающий одно целое число, даёт наилучшие результаты с точки зрения низких накладных расходов при профилировании. ([`os.times()`](https://python-all.ru/2.7/library/os.html#os.times) *довольно* плох, так как он возвращает кортеж значений с плавающей запятой). Если требуется заменить лучший таймер наиболее чистым способом, создайте подкласс и жёстко пропишите метод диспетчеризации, который наилучшим образом обрабатывает вызов вашего таймера, вместе с соответствующей константой калибровки.409410**`cProfile.Profile`**411412`your_time_func` должна возвращать одно число. Если она возвращает целые числа, можно также вызвать конструктор класса со вторым аргументом, указывающим реальную длительность одной единицы времени. Например, если `your_integer_time_func` возвращает время, измеряемое в тысячах секунд, экземпляр `Profile` конструируется следующим образом:413414```python415pr = cProfile.Profile(your_integer_time_func, 0.001)416```417418Поскольку класс `cProfile.Profile` не может быть откалиброван, пользовательские функции таймера следует использовать с осторожностью, и они должны быть максимально быстрыми. Для достижения наилучших результатов с пользовательским таймером может потребоваться жёстко задать его в исходном коде C внутреннего модуля `_lsprof`.419420Сноски421422**[1](https://python-all.ru/2.7/library/profile.html#id1)**423424До Python 2.2 необходимо было редактировать исходный код профилировщика, чтобы встроить смещение как литеральное число. Это всё ещё возможно, но этот метод больше не описывается, так как больше не нужен.425