Перейти к содержанию

Команды

У takt три команды: takt run, takt import и takt compare. takt --version выводит takt <версия>.

takt run

takt run запускает бенчмарки и записывает результат во все выбранные БД.

Два режима

takt убирает свои флаги (см. «Флаги» ниже) и смотрит на первый оставшийся аргумент:

  • Если он оканчивается на .py и не начинается с -, takt считает его путём к pyperf-скрипту (скрипту на pyperf.Runner). takt запускает его тем же Python, в котором работает сам.
  • Иначе takt запускает pyperformance run.

Режим pyperformance:

takt run -b nbody,json_dumps --fast --target local

Режим pyperf-скрипта:

takt run bench_sort.py --values 5 --db sqlite:///bench.db

Флаги

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

Флаг Можно повторять Что делает
--db URL да SQLAlchemy URL целевой БД
--target NAME да выбрать цель из takt.toml по имени
--config PATH нет путь к takt.toml вместо ./takt.toml
--name TEMPLATE нет имя прогона или шаблон имени

Флаги takt не совпадают с флагами pyperformance и pyperf. Сокращения флагов отключены: --ta не считается --target. takt раскрывает ~ в каждом пути из командной строки: в --config, в файле takt import, в --markdown, в операндах takt compare и в -o/--output. Поэтому работает и --config=~/takt.toml, хотя оболочка оставляет ~ после = как есть. Как выбираются цели и имя, описано на странице Настройка.

takt проверяет цели и текст шаблона имени до запуска бенчмарков. Если там ошибка, команда завершается с кодом 2, и ни один бенчмарк не запускается. Лимит 255 символов и :, который пришёл из значения вроде {hostname}, проверяются только после бенчмарков: см. «Имя прогона» на странице Настройка.

Файл результата

Если флаг -o/--output не указан, результат пишется в ./takt-<YYYYmmddTHHMMSSZ>.json, время в UTC. takt никогда не удаляет файл результата.

takt понимает -o FILE, -oFILE, --output FILE, --output=FILE, сокращённое --out FILE и -o, склеенный с другими короткими флагами, например -fo FILE. Он раскрывает ~ и превращает относительный путь в абсолютный от текущего каталога. Потом takt добавляет --output <абсолютный путь> после остальных флагов команды бенчмарков, перед разделителем --, если он есть. pyperformance и pyperf берут последний --output, поэтому результат попадает ровно в тот файл, который читает takt.

Каталог файла результата должен уже существовать, и у takt должно быть право писать в него. Без -o это текущий каталог. takt проверяет это до запуска бенчмарков, потому что pyperformance записывает файл только после последнего бенчмарка. Иначе команда завершается с кодом 2 и печатает error: cannot create result file <файл>: folder <каталог> does not exist; choose another file with -o или ту же ошибку с is not writable. takt не создаёт каталог сам: иначе опечатка в пути осталась бы незамеченной.

Вывод pyperformance и pyperf идёт в терминал как есть. Когда бенчмарки закончились, takt приводит схему каждой БД к текущей версии и записывает результат.

Вывод

Result 3fa2b1c4d5e6 (nightly 2026-09-25) from /home/me/takt-20260925T101500Z.json
  local: written
  maria_ci: written

В первой строке — первые 12 символов хеша результата, имя прогона и файл результата. Прогон без имени показывается как unnamed. Если результат уже был во всех БД, ничего не записано, поэтому первая строка показывает имя, под которым он там сохранён, а не то, которое вы указали; если в БД сохранены разные имена, имени в строке нет. Дальше идёт по одной строке на каждую цель:

Текст Что значит
written результат записан
already loaded as '<имя>' в этой БД результат уже был под этим именем
already loaded (unnamed) в этой БД результат уже был без имени
failed: <ошибка> запись в эту БД не удалась
failed: compensation failed: <ошибка> результат был записан, но после ошибки в другой БД takt не смог его удалить
rolled back результат был записан, но удалён, потому что в другой БД произошла ошибка
not attempted takt остановился раньше, чем дошёл до этой БД

После compensation failed результат остаётся в этой БД: см. «Всем или никому» ниже.

Если запись не удалась

Если результат записался не во все цели, takt показывает причину для каждой цели, а потом пишет в stderr:

error: result was not written to all targets
Retry without re-running benchmarks: takt import /home/me/takt-20260925T101500Z.json --target local --target maria_ci --name 'nightly 2026-09-25'

Команда завершается с кодом 1. Файл результата остаётся на диске: почините БД и выполните напечатанную команду takt import. Бенчмарки заново не запускаются.

Напечатанная команда передаёт имя прогона, которое эта команда дала результату, а не шаблон имени: с nightly {date} повтор на следующий день всё равно сохранит nightly 2026-09-25. Фигурные скобки в этом имени удвоены, поэтому остаются обычным текстом. Если у результата нет имени, в команде нет --name.

Если бенчмарки упали

Если pyperformance или pyperf-скрипт завершился с ошибкой, takt ничего не пишет в БД, показывает ошибку и завершается с кодом 1. Если упавший процесс всё же успел записать новый файл результата, takt пишет в stderr ещё одну строку:

error: benchmark command failed with exit code 1: /usr/bin/python3 -m pyperformance run -b nbody,json_dumps --output /home/me/takt-20260925T101500Z.json
Partial result was written to /home/me/takt-20260925T101500Z.json. Load it without re-running benchmarks: takt import /home/me/takt-20260925T101500Z.json --target local

В файле может не быть упавших бенчмарков. Если остальных вам хватает, выполните напечатанную команду takt import.

После Ctrl+C

Если нажать Ctrl+C во время бенчмарков, takt ничего не пишет в БД и завершается с кодом 130. pyperf-скрипт записывает каждый законченный бенчмарк в файл результата сразу, поэтому после Ctrl+C в файле уже могут быть готовые бенчмарки. Если на диске есть новый файл результата, takt печатает только строку Partial result was written to …, без строки error:. pyperformance пишет свой файл только в конце, поэтому после Ctrl+C файла обычно нет, и такой строки тоже нет.

Если нажать Ctrl+C уже после бенчмарков, пока takt записывает результат в БД, например ждёт заблокированную БД, файл результата полный. Тогда takt пишет в stderr такую строку и завершается с кодом 130:

Result was written to /home/me/takt-20260925T101500Z.json. Load it without re-running benchmarks: takt import /home/me/takt-20260925T101500Z.json --target local --name 'nightly 2026-09-25'

Как и команда повтора выше, она передаёт имя прогона, которое эта команда дала результату. Если Ctrl+C пришёлся на подтверждение записи, в части БД результат уже может быть; напечатанная команда их пропустит (см. «Всем или никому» ниже).

takt import

takt import записывает готовый результат pyperf или pyperformance во все выбранные БД.

takt import result.json --db sqlite:///bench.db --name patched
  • Файл может быть .json или сжатым gzip .json.gz.
  • Флаги --db, --target, --config и --name те же, что у takt run.
  • Вывод такой же, как у takt run.
  • takt отказывается принимать файл, который не является корректным результатом pyperf: например, если замер равен NaN или бесконечности или имя бенчмарка не строка. Он пишет error: invalid pyperf result <файл>: <причина> и завершается с кодом 1.
  • Если файл не открывается, не распаковывается или не читается как JSON, takt пишет error: cannot read pyperf result <файл>: <причина> и завершается с кодом 1.

Хеш результата

takt узнаёт результат по хешу: это SHA-256 от содержимого JSON с отсортированными ключами и без пробелов. Имя файла и его форматирование на хеш не влияют. У файла .json и его копии .json.gz хеш одинаковый.

Повторный импорт

takt проверяет каждую БД отдельно. Если результат в БД уже есть, takt её пропускает и пишет already loaded as '<имя>'. Сохранённое имя не меняется, даже если передать другое --name. Если результат уже есть во всех БД, команда ничего не пишет и завершается успешно.

Всем или никому

takt записывает результат либо во все выбранные БД, либо ни в одну:

  1. takt открывает транзакцию в каждой БД и вставляет результат.
  2. Если вставка упала хотя бы в одной БД, takt откатывает все транзакции и завершается с ошибкой.
  3. Потом takt по очереди подтверждает (commit) транзакции.
  4. Если подтверждение упало, когда другие БД уже подтверждены, takt удаляет результат из этих БД.

В этих случаях результат всё же остаётся только в части БД:

  • takt не смог удалить результат на шаге 4. У такой БД статус failed: compensation failed: <ошибка>.
  • Процесс takt убили или прервали по Ctrl+C во время подтверждения. Тогда takt не печатает отчёт, а после Ctrl+C завершается с кодом 130, и takt run ещё печатает строку Result was written to ….

После compensation failed строка Result упавшей команды показывает первые 12 символов хеша результата и имя, под которым он сохранился в этой БД. После остановки такой строки нет. Остановленная команда записала результат с одним и тем же временем loaded_at во все БД, до которых дошла, поэтому в этих БД этот запрос покажет его как самый новый результат:

SELECT hash, name, loaded_at FROM takt_suite ORDER BY loaded_at DESC LIMIT 1;

Чтобы результат был во всех БД, ещё раз выполните takt import для файла результата: он пропустит БД, где результат уже есть, и допишет его в остальные. Если в шаблоне имени есть {date} или {datetime}, takt подставит в них новое время, и в остальных БД имя может получиться другим. Чтобы в остальных БД имя было таким же, передайте сохранённое имя через --name; команда, которую печатает takt run после compensation failed или Ctrl+C, уже делает это.

Чтобы вместо этого убрать результат, выполните эти запросы в этом порядке и только в тех БД, где его оставила упавшая команда: со статусом compensation failed или, после остановки, там, где запрос выше показывает его со временем loaded_at этой команды. Не выполняйте их в БД со статусом already loaded: там результат был ещё до этой команды. Дочерние таблицы идут первыми: MariaDB не удаляет строку, пока на неё ссылаются другие строки. Вместо 3fa2b1c4d5e6 подставьте первые 12 символов хеша результата.

DELETE FROM takt_measurement WHERE suite_hash LIKE '3fa2b1c4d5e6%';
DELETE FROM takt_run_metadata WHERE suite_hash LIKE '3fa2b1c4d5e6%';
DELETE FROM takt_worker_run WHERE suite_hash LIKE '3fa2b1c4d5e6%';
DELETE FROM takt_benchmark WHERE suite_hash LIKE '3fa2b1c4d5e6%';
DELETE FROM takt_suite WHERE hash LIKE '3fa2b1c4d5e6%';
DELETE FROM takt_loaded_hash WHERE hash LIKE '3fa2b1c4d5e6%';

takt compare

takt compare сравнивает два или больше результата. Первый операнд — базовый, все остальные сравниваются с ним.

takt compare 3fa2b1 "default 01.01.01" "jit+pgo:1" ~/path/to/res.json --target local

Операнды

takt проверяет каждый операнд в таком порядке и берёт первое совпадение:

Операнд Что это
results/a.json, ~/r.json.gz файл на диске, если такой файл есть
default прогон с именем default; если таких прогонов несколько — ошибка со списком вариантов
default:0, default:2 N-й прогон с именем default, счёт с 0, по дате результата
default:3fa2b1 прогон с именем default, у которого хеш начинается с этих символов (от 6)
3fa2b1 прогон, у которого хеш начинается с этих символов (от 6 строчных hex-символов)

Если после : только цифры, это номер; всё остальное — начало хеша. Операнд из 1–5 hex-символов или с заглавными буквами никогда не ищется как начало хеша. Если прогона с таким именем нет, а в операнде есть и цифры, и буквы от a до f, ошибка подсказывает, как найти прогон по хешу: no file or run name matches; to find a run by hash, use 6 to 64 lowercase hex characters. Операнд, похожий на путь к файлу результата, никогда не считается прогоном: он кончается на .json или .json.gz либо начинается с /, ./, ../ или ~/. Если такого файла нет, takt пишет error: result file not found: <операнд> и завершается с кодом 1, есть БД или нет. В имени прогона / при этом допустим, например release/3.14. Операнд, который не подходит ни под одну форму, — ошибка с кодом 1: например, пустой операнд, default:, :1, a:b:c или default:XYZ. Прогоны с одинаковой датой результата упорядочены по хешу, поэтому номера одинаковые во всех БД. Прогоны без даты результата идут последними; в списке вариантов у них написано unknown date.

Если имени соответствуют несколько прогонов, takt их перечисляет:

error: operand 'default' is ambiguous, candidates:
  default:0  2026-09-20 10:00:00  3fa2b1c4d5e6
  default:1  2026-09-25 10:00:00  9c01de7a8b2f

Файлы и БД

  • В одном сравнении можно смешивать файлы и прогоны из БД.
  • Прогоны читаются из первой цели в списке. Другую цель выбирает --target.
  • Если прогон не найден, ошибка называет ту единственную цель, где takt его искал, например error: operand 'o1' not found in target 'local': no file, run name or hash prefix matches.
  • takt compare только читает БД и никогда не создаёт файл SQLite. Опечатка в пути SQLite даёт error: cannot connect to database <цель>: database file not found: <путь>, а БД без таблиц takt — error: no takt results in database <цель>; код выхода 1.
  • Если все операнды — файлы, БД не нужна, и takt не читает --db, --target, --config, TAKT_DB и takt.toml.
  • Если операнд не файл, а ни одной цели не задано, takt пишет error: operand '<операнд>' is not a file and no database target is configured и завершается с кодом 2. Операнд, который не подходит ни под одну форму, и тогда даёт код 1: форму всех операндов takt проверяет раньше.

Вывод

takt выводит таблицу в терминал. Если вывод идёт в файл или в конвейер, как в логе CI, takt не переносит таблицу и строки под ней, поэтому каждый заголовок столбца остаётся целым. В узком терминале длинный заголовок столбца переносится на несколько строк; takt никогда его не обрезает. С --markdown PATH он ещё записывает таблицу в Markdown-файл и пишет Markdown table written to <PATH>.

takt compare base.json new.json --markdown compare.md

Пример Markdown-файла:

| Benchmark      | base.json | new.json              |
|----------------|:---------:|:---------------------:|
| nbody          | 100 ms    | 90.0 ms: 1.11x faster |
| Geometric mean | (ref)     | 1.04x faster          |

Benchmark hidden because not significant (2): a, b
Ignored benchmarks (1) of new.json: x

Как читать таблицу:

  • Заголовок столбца — операнд ровно так, как вы его ввели.
  • В Markdown-файле takt пишет | в заголовке или в имени бенчмарка как \|, чтобы таблица не развалилась на лишние столбцы; программа просмотра Markdown показывает его как |.
  • В Markdown-файле между таблицей и строками под ней всегда есть пустая строка, чтобы программа просмотра Markdown не показала эти строки как строки таблицы.
  • В базовом столбце — среднее значение.
  • В остальных столбцах — среднее значение и изменение относительно базового: 1.11x faster (быстрее), 1.05x slower (медленнее), no change (без изменений) или not significant (разница незначима).
  • Строка Geometric mean (среднее геометрическое) появляется, если общих бенчмарков больше одного и хотя бы один из них есть в таблице.
  • Benchmark hidden because not significant — бенчмарки, где ни одна разница не значима; в таблице их нет.
  • Ignored benchmarks … of <операнд> — бенчмарки этого операнда, которые не сравнивались. Сравниваются только бенчмарки, у которых во всех операндах есть замеры: если у какого-то операнда бенчмарка нет или у него есть только значения прогрева (warmup), бенчмарк пропускается.

Если сравнивать нечего, takt таблицу не печатает: он пишет error: benchmark suites have no benchmark in common и завершается с кодом 1. Так бывает, даже если у результатов есть бенчмарки с одинаковыми именами, но у какого-то операнда для каждого из них есть только значения прогрева.

Числа и проверка значимости такие же, как у pyperf compare_to --table.

Коды выхода

Код Когда
0 успех, в том числе «всё уже было загружено»
1 ошибка выполнения: БД, файл, бенчмарк, запись не во все цели, операнд compare не подходит ни под одну форму, не найден или неоднозначен, у результатов в compare нет общих бенчмарков
2 ошибка аргументов или конфигурации, найдена до запуска бенчмарков, итоговое имя прогона нарушает правила, или операнд compare не файл, а ни одной цели не задано
130 прервано по Ctrl+C

Ошибку в самой командной строке, например пропущенный аргумент или флаг без значения, показывает argparse. Он печатает подсказку по использованию команды (usage), а под ней строку вроде takt import: error: the following arguments are required: PATH. При неизвестном флаге у takt import или takt compare печатается общая подсказка takt и строка takt: error: unrecognized arguments: <флаги>. takt run неизвестные флаги не отклоняет: он передаёт их pyperformance или pyperf-скрипту. -o/--output у takt run проверяет сам takt: если пути к файлу нет, он печатает только строку error: option -o/--output requires a file path. Код выхода 2.

Любая другая ошибка выводится в stderr как error: <сообщение>, без трейсбека. Обычно это одна строка; у неоднозначного операнда под ней ещё идёт список вариантов. Если takt run не смог записать результат или бенчмарки упали, но файл результата всё же записан, takt ещё печатает команду takt import: она загружает этот файл без повторного запуска бенчмарков. Если после бенчмарков итоговое имя прогона нарушает правила, takt печатает путь к файлу результата: загрузите его через takt import с другим --name.