ГлавнаяБлогПочему бенчмарк останавливался на 22: 9 багов в одном тесте
Python

Почему бенчмарк останавливался на 22: 9 багов в одном тесте

Разбираем 9 скрытых багов в бенчмарке A2A на Python, Go, Node и Rust. Узнайте, как одна строчка кода искажала результаты годами. Исправьте свои тесты прямо сейчас.

Al
Редакция Algolitalgolit.ru
12 мин чтения16 июля 2026 г.

Представьте: вы запускаете бенчмарк, видите красивый график и уверены, что всё работает идеально. Но однажды вы замечаете странность — тест останавливается на N=22, хотя должен идти до 24. Вы копаете глубже и находите 9 багов, которые делали каждый график неверным. В этой статье мы разберём, как одна строчка кода может скрывать проблему годами, и научимся писать бенчмарки, которым можно доверять.

Странное число 22: как баг маскировался под осознанное решение

В репозитории a2a-benchmark лежал файл run_benchmark_1_22.py. Не 1_24, как ожидалось по логике теста, и не 1_26 по количеству известных экспонент Мерсенна. Просто 22. И подтверждающий график — a2a_latency_times_1_22.png. Кто-то решил, что бенчмарк должен заканчиваться на 22, зафиксировал это в коде и забыл.

Но истинная причина оказалась прозаичнее: краш. При N=24 Python-агент падал с ошибкой:

"Exceeds the limit (4300 digits) for integer string conversion;
 use sys.set_int_max_str_digits() to increase the limit"

CPython 3.11 ввёл ограничение на преобразование int в str — 4300 цифр — как защиту от DoS-атак. А 24-е простое Мерсенна, 2^19937−1, содержит 6002 цифры. Агент преобразовывал каждое найденное простое в строку — и падал.

Самое смешное: эта строчка была декоративной. Результат преобразования нигде не использовался — агент отчитывался только о времени выполнения. Исправление заняло одну секунду: удалить str() и оставить сырое число. Go-версия имела точно такой же мёртвый код (val.String()), но ей просто повезло не упасть.

После удаления одной строчки появился столбец данных, которого никогда не существовало: N=24, Python, 2425.9 мс.

Четыре способа, которыми бенчмарк врал

1. Парсинг времени через LLM

Харнес извлекал время выполнения Python из текста ответа LLM с помощью регулярного выражения r"It took ([\d\.\-e]+) seconds". Но Gemini в моих логах ни разу не сказала "It took" — она писала "Calculating the first 5 Mersenne primes took…" или "The calculation took…". Единственная причина, по которой бенчмарк вообще имел данные по Python, — fallback на чтение структурированного артефакта инструмента. Основной путь измерения был ставкой на речевые привычки языковой модели.

2. Ложные отчёты о количестве

Node и Rust-агенты при запросе 100 простых чисел радостно отвечали "Found first 100 Mersenne primes", хотя на самом деле вычисляли только 26 — размер таблицы экспонент. А время они форматировали как %.2f ms, поэтому быстрейшие запуски Rust показывали 0.00ms, что на логарифмической шкале выглядело как отсутствие данных, а не ошибка.

3. Сравнение яблок с апельсинами

График назывался "A2A Round-Trip Time (including LLM/Tool calling)", но два из четырёх агентов (Python и Go) шли через Gemini, а два других (Node и Rust) — напрямую через HTTP. Медианное время: ~2.6 мс и ~4.6 мс для прямых, ~1.6 с и ~1.8 с для Gemini. Разрыв в 400 раз был не сравнением языков, а сравнением архитектур.

4. Бенчмарк стал жертвой собственной оптимизации

После исправления Go-агент на N=1 отработал так быстро, что time.Duration переключил единицы — "Elapsed time: 836ns". А харнес умел парсить только µs, ms и s. Наносекунд не было. Исправление сделало код слишком быстрым для собственного бенчмарка.

Ещё один сюрприз: "Я уже это делал"

После повторного прогона пропали три точки данных Go. Причина: харнес переиспользовал идентификаторы контекста, ADK хранит историю сессии, и Gemini, увидев старый разговор, отказался повторять работу: "I already did that. Do you want to do it again?" Зависимость бенчмарка от мнения LLM о повторении — это новый уровень хрупкости. Уникальные ID на каждый запуск решили проблему, и финальный прогон вернул 96/96 точек.

Что я вынес из этой истории

  • Заплатка, которую вы закоммитили, — это баг, который вы сохранили. Файл run_benchmark_1_22.py лежал в репозитории как окаменелость неисследованного краша. Как только вы переименовываете скрипт вместо чтения stack trace, вы решаете поставлять баг.
  • Бенчмарки — это production-код. Мой падал, врал о количестве, округлял свои малые измерения и сравнивал две разные архитектуры по одной оси. Каждый график, который он когда-либо создавал, был тихо неверен четырьмя способами.
  • Если вывод потребляет машина, никогда не маршрутизируйте его через прозу. Структурированные артефакты инструментов существовали всё время; регулярка на текст LLM была чистой хрупкостью.
  • LLM в измерительном контуре добавляет режимы отказа, не связанные с тестируемым кодом — включая, судя по всему, скуку.

Девять багов. Четыре PR (#1, #2, #3, #4). Один вопрос, который следовало задать год назад: почему 22?

Практический вывод

Прямо сейчас откройте свой самый старый бенчмарк и задайте себе три вопроса:

  1. Есть ли в нём мёртвый код (преобразования, логи, форматирование), который может упасть?
  2. Парсите ли вы вывод через регулярки по тексту, когда можно использовать структурированные данные?
  3. Сравниваете ли вы действительно одно и то же (одинаковый pipeline, одинаковые условия)?

Если ответ на любой вопрос "да" — исправьте это сегодня. Иначе ваш бенчмарк может тихо врать годами, а вы будете думать, что число 22 — это осознанное решение.

# Пример: как НЕ надо парсить время из LLM
import re

def parse_time_llm(text):
    # Хрупко: зависит от формулировки LLM
    match = re.search(r"It took ([\d\.\-e]+) seconds", text)
    if match:
        return float(match.group(1))
    # fallback — тоже хрупко
    return None

# Лучше: использовать структурированный артефакт
# (например, JSON-ответ инструмента)
def parse_time_structured(response):
    return response.tool_output.elapsed_ms
#бенчмарки#баги#Python#тестирование#LLM
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →