Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Внутри CPython гид по интерпретатору Python.pdf
Скачиваний:
7
Добавлен:
07.04.2024
Размер:
8.59 Mб
Скачать

Бенчмаркинг, профилирование и трассировка

При внесении изменений в CPython необходимо следить за тем, чтобы эти изменения не имели отрицательного воздействия на производительность. Возможно, вам даже захочется изменить CPython так, чтобы производительность улучшилась.

Вэтой главе рассматриваются различные средства профилирования:

1.Использование модуля timeit для многократной проверки простых выражений Python и вычисления медианной скорости выполнения.

2.Выполнение pyperformance (набора тестов Python) для сравнения разных версий Python.

3.Анализ времени выполнения кадров с использованием cProfile.

4.Профилирование выполнения CPython при помощи датчиков.

Выбор решения зависит от типа задачи:

zz Бенчмарк выдает среднее или медианное время выполнения фиксированного фрагмента кода, чтобы вы могли сравнить разные варианты.

zz Профайлер строит граф вызовов с указанием времени выполнения, чтобы вы понимали, какая функция работает медленнее всего.

Профайлеры доступны на уровне C и на уровне Python. Если вы профилируете функцию, модуль или скрипт, написанные на Python, необходимо использовать профайлер Python. Если же профилируется модуль расширения

Книги для программистов: https://t.me/booksforits

316    Бенчмаркинг, профилирование и трассировка

или модификация кода C в CPython, вам понадобится профайлер C или комбинация профайлеров С и Python.

Краткий перечень некоторых доступных инструментов:

ИНСТРУМЕНТ

КАТЕГОРИЯ

УРОВЕНЬ

ПОДДЕРЖКА ОС

timeit

Бенчмарк

Python

Все

pyperformance

Бенчмарк

Python

Все

cProfile

Профилирование

Python

Все

DTrace

Трассировка/профи-

C

Linux, macOS

 

лирование

 

 

ВАЖНО

Прежде чем выполнять какие-либо тесты,лучше закрыть все приложения накомпьютере,чтобыпроцессорбылполностьювыделендлябенчмарка.

ИСПОЛЬЗОВАНИЕ TIMEIT ДЛЯ МИКРОБЕНЧМАРКА

Набор тестов производительности Python тщательно проверяет общую оперативность среды выполнения CPython, делая множество итераций. Если вы хотите провести быстрое и простое сравнение конкретного фрагмента, используйте модуль timeit.

Чтобы применить timeit к короткому скрипту, запустите скомпилированную версию CPython с модулем -m timeit и заключите данный скрипт в кавычки:

$ ./python -m timeit -c "x=1; x+=1; x**x" 1000000 loops, best of 5: 258 nsec per loop

Чтобы уменьшить количество циклов, используйте флаг -n:

$ ./python -m timeit -n 1000 "x=1; x+=1; x**x" 1000 loops, best of 5: 227 nsec per loop

Книги для программистов: https://t.me/booksforits

Использование timeit для микробенчмарка    317

Пример использования timeit

В этой книге мы внесли изменения в тип float, добавив поддержку оператора «почти равно».

Попробуйте выполнить этот тест, чтобы оценить производительность сравнения двух чисел с плавающей точкой:

$ ./python -m timeit -n 1000 "x=1.0001; y=1.0000; x~=y" 1000 loops, best of 5: 177 nsec per loop

Реализация этого сравнения содержится в функции float_richcompare(), внутри Objects floatobject.c:

Objects floatobject.c, строка 358

static PyObject*

float_richcompare(PyObject *v, PyObject *w, int op)

{

...

case Py_AlE: {

double diff = fabs(i - j); double rel_tol = 1e-9; double abs_tol = 0.1;

r = (((diff <= fabs(rel_tol * j)) || (diff <= fabs(rel_tol * i))) ||

(diff <= abs_tol));

}

break;

}

Обратите внимание: значения rel_tol и abs_tol являются константами, но не имеют соответствующей пометки. Приведите их к следующему виду:

const double rel_tol = 1e-9; const double abs_tol = 0.1;

Теперь снова скомпилируйте CPython и заново выполните тест:

$ ./python -m timeit -n 1000 "x=1.0001; y=1.0000; x~=y" 1000 loops, best of 5: 172 nsec per loop

Наблюдается небольшое (от 1 до 5 процентов) улучшение производительности. Поэкспериментируйте с разными реализациями сравнения и посмотрите, удастся ли вам добиться еще большего прироста.

Книги для программистов: https://t.me/booksforits

318    Бенчмаркинг, профилирование и трассировка

ИСПОЛЬЗОВАНИЕ НАБОРА ТЕСТОВ ПРОИЗВОДИТЕЛЬНОСТИ PYTHON

Набор тестов Python (Python Benchmark Suite) поможет оценить общую производительность Python. В него включены приложения Python, разработанные для тестирования нескольких аспектов среды выполнения Python при нагрузке.

Тесты производительности из набора написаны на чистом Python, что позволяет использовать их для тестирования разных сред выполнения, например PyPy и Jython. Кроме того, они совместимы с Python от 2.7 до последней версии.

Любые коммиты в главной ветви github.com/python/cpython тестируются с использованием инструментов бенчмарка, а результаты отправляются в Python Speed Center1:

1 https://speed.python.org.

Книги для программистов: https://t.me/booksforits

Использование набора тестов производительности Python    319

ВSpeed Center можно параллельно сравнить различные коммиты и ветви.

Вбенчмарках применяется как профильная оптимизация, так и обычные сборки с фиксированной конфигурацией оборудования для обеспечения стабильности сравнений.

Набор тестов производительности можно установить из PyPI с использованием среды выполнения Python (не той, которую вы тестируете) в виртуальной среде:

(venv) $ pip install pyperformance

Затем необходимо создать файл конфигурации и выходной каталог для тестового профиля. Рекомендуется создать этот каталог за пределами вашей рабочей директории Git. Это также позволит вам извлечь из репозитория несколько разных версий.

Добавьте в файл конфигурации (например, ~/benchmarks/benchmark.cfg) следующие строки:

cpython-book-samples 62 benchmark.cfg

[config]

#Путь к выходным файлам json json_dir = ~/benchmarks/json

#Если True, скомпилировать CPython в отладочном режиме (без LTO и PGO),

#запустить тесты с --debug-single-sample и заблокировать отправку

#

# Используется для быстрого тестирования конфигурации debug = False

[scm]

#Каталог с исходным кодом CPython (репозиторий Git) repo_dir = ~/cpython

#Обновить репозиторий Git (git fetch)?

update = False

#Имя удаленного репозитория Git, используемое для создания

#ревизии ветви Git

git_remote = remotes/origin

[compile]

#Создать файлы в bench_dir: bench_dir = ~/benchmarks/tmp

#Применять оптимизацию на стадии компоновки кода (LTO)? lto = True

Книги для программистов: https://t.me/booksforits

320    Бенчмаркинг, профилирование и трассировка

#Применять профильную оптимизацию (PGO)? pgo = True

#Разделенный пробелами список пакетных библиотек pkg_only =

#Установить Python? Если False, то Python запускается из каталога сборки install = True

[run_benchmark]

#Выполнить "sudo python3 -m pyperf system tune" перед запуском тестов? system_tune = True

#Параметр --benchmarks для 'pyperformance run'

benchmarks =

#Параметр --affinity для 'pyperf system tune' и 'pyperformance run' affinity =

#Отправить сгенерированный файл JSON?

upload = False

# Конфигурация для отправки результатов на сайт Codespeed

[upload] url =

environment = executable = project =

[compile_all]

#Список ветвей CPython в Git branches = default 3.6 3.5 2.7

#Список ревизий для compile_all

[compile_all_revisions]

#Список 'sha1=' (ветвь по умолчанию: 'master') или 'sha1=branch',

#используемый командой "pyperformance compile_all"

Проведение бенчмарка

Когда файл конфигурации настроен, можно выполнить тесты:

$ pyperformance compile -U ~/benchmarks/benchmark.cfg HEAD

Команда компилирует CPython в заданном каталоге repo_dir и формирует выходной файл в формате JSON, помещая данные бенчмарка в каталог, заданный в файле конфигурации.

Книги для программистов: https://t.me/booksforits

Использование набора тестов производительности Python    321

Сравнение результатов

В набор тестов производительности не входят инструменты для построения графов, поэтому если вы хотите сравнить результаты в формате JSON, в пределах виртуальной среды можно использовать приведенный ниже скрипт.

Сначала установите зависимости:

$ pip install seaborn pandas pyperformance

Затем создайте скрипт profile.py:

cpython-book-samples 62 profile.py

import argparse

from pathlib import Path

from perf._bench import BenchmarkSuite

import seaborn as sns import pandas as pd

sns.set(style="whitegrid")

parser = argparse.ArgumentParser() parser.add_argument("files", metavar="N", type=str, nargs="+",

help="files to compare") args = parser.parse_args()

benchmark_names = [] records = []

first = True

for f in args.files:

benchmark_suite = BenchmarkSuite.load(f) if first:

# Инициализировать ключи словаря именами бенчмарков benchmark_names = benchmark_suite.get_benchmark_names() first = False

bench_name = Path(benchmark_suite.filename).name for name in benchmark_names:

try:

benchmark = benchmark_suite.get_benchmark(name) if benchmark is not None:

records.append({ "test": name,

"runtime": bench_name.replace(".json", ""), "stdev": benchmark.stdev(),

"mean": benchmark.mean(), "median": benchmark.median()

}) except KeyError:

Книги для программистов: https://t.me/booksforits