Бенчмарки

Точность и время: результаты конкретного прогона

Таблицы получены скриптом scripts/generate-benchmarks.py 2026-09-08на опубликованном Balansis 1.1.0, Python 3.12.3 и NumPy 1.26.4. Это снимок поведения выбранного релиза, а не оценка всех версий библиотеки и всех типов входных данных.

Как это меряется

  • Эталон точности — рациональная арифметика. Ошибка каждого метода считается против значения, вычисленного в fractions.Fraction, а не против «более длинного float». Иначе эталон сам был бы участником соревнования. Единственное место, где рациональной арифметики недостаточно, — softmax: экспонента там считается в decimal с 60 значащими цифрами (на 40+ порядков точнее float64), а суммирование и деление — там же.
  • Базовые методы. Для сумм измерены наивный цикл, math.fsum, встроенный sum() и NumPy. С Python 3.12 float-сумма в sum улучшена, поэтому это отдельный метод, а не синоним наивного цикла. math.sumprod, также добавленный в 3.12, здесь не измерен: он может решать показанную задачу произведений без Balansis. Эта таблица не устанавливает преимущества над всей стандартной библиотекой. Контракт sumprod.
  • Время — минимум из серии. Хост разделяемый (Intel Xeon (серверный), 8 vCPU), поэтому headline-значение — наименьшее время из повторов, как наименее зашумлённое; медиана лежит рядом в JSON. Абсолютные значения зависят от машины, как и отношения между методами. Поправка из следующего пункта оценивает один источник накладных расходов, но не делает результат независимым от окружения.
  • Накладные вызова вычтены. Обвязка измерения вызывает питоновскую функцию на каждую операцию; пустой вызов в ней стоит 15.2 нс — столько же, сколько само сложение float. Поэтому в таблицах цены обе колонки уменьшены на эту величину, а отношение «как есть» показано отдельной строкой. Проверка: сложение float в скомпилированном цикле timeit без вызова на операцию даёт 10.5 нс против 11.0 нс после вычитания.
  • Проверяется библиотека с PyPI, а не рабочая копия. Прогон идёт в чистом venv, куда установлено колесо balansis 1.1.0; sha256 скачанного файла сверен с digest, который объявляет JSON-API PyPI (2445ae239cb21e48…, совпал).

Точность: сводка по сценариям

Оценка верных десятичных знаков по относительной ошибке, ограниченная диапазоном 0–16. Измерено 2026-09-08
СценарийМетодВерных знаковАбс. ошибкаВремя (min)
Сумма 300 слагаемых со взаимным уничтожениемнаивный цикл float640.01003.11 мкс
встроенный sum()16 (предел float64)01.11 мкс
numpy.sum (попарное суммирование)0.0918.32 мкс
math.fsum16 (предел float64)01.57 мкс
Balansis Operations.sequence_sum16 (предел float64)039.31 мкс
Скалярное произведение, cond ≈ 5e18наивный цикл float640.031.469437.81 мкс
numpy.dot (BLAS)0.031.4694643.0 нс
math.fsum над произведениями0.07.8444151.89 мкс
Balansis dot2 (Ogita–Rump–Oishi)16 (предел float64)4.478e-1787.65 мкс
Balansis numpy_integration.compensated_dot_product16 (предел float64)4.478e-1787.57 мкс
Преимущество среди измеренных методов видно в скалярном произведении. Там Balansis даёт 16 (предел float64) против нуля у numpy и у math.fsum. В сценарии суммирования победа делится: точный ответ дают и math.fsum, и встроенный sum(), и Balansis — разница только во времени, и она не в пользу Balansis (25×). Сравнение с math.sumprod нужно провести отдельно.

Цена компенсации

Скалярный слой — арифметика на объектах AbsoluteValue. Векторный — мост к numpy, через который проходят реальные пайплайны. Это две очень разные цены, и смешивать их в одну цифру «Balansis в N раз медленнее» было бы неправдой.

Скалярные операции, одна операция. Время — без накладных питоновского вызова (15.2 нс), они вычтены из обеих колонок. Измерено 2026-09-08
ОперацияЧто вызывается в Balansisfloat64BalansisЗамедление
сложение двух чиселOperations.compensated_add11.0 нс954.3 нс87×с накладными вызова: 37×
конструирование значенияAbsoluteValue.from_float (модель pydantic)7.8 нс884.4 нс114×с накладными вызова: 39×
two_sum (примитив EFT)two_sum — чистый float, без объектов10.8 нс64.2 нс5.9×с накладными вызова: 3.0×
two_product (примитив EFT)two_product — чистый float, без объектов10.8 нс131.6 нс12×с накладными вызова: 5.7×
Векторные операции на массивах по 100 000 элементов. Здесь накладные вызова на пять-шесть порядков меньше самой операции и на отношение не влияют. Измерено 2026-09-08
ОперацияЧто вызывается в Balansisfloat64BalansisЗамедление
поэлементное сложение массивов (n=100 000)compensated_array_add46.03 мкс927.20 мкс20×с накладными вызова: 20×
скалярное произведение (n=100 000)compensated_dot_product13.73 мкс11.28 мс821×с накладными вызова: 820×
softmax (n=100 000)compensated_softmax901.47 мкс21.19 мс24×с накладными вызова: 24×
Самая дорогая строка честнее всего описывает библиотеку. Компенсированное скалярное произведение на 100 000 элементах медленнее np.dot примерно в 820× — потому что BLAS считает в SIMD, а dot2 раскладывает каждое произведение на пару «значение + поправка» и суммирует компоненты. Это измеренная стоимость конкретной реализации, а не неизбежная цена точности. dot2 этого релиза не гарантирует корректного округления на любых входах. Применение оправдано, если проверка на ваших данных показывает нужный выигрыш по точности при приемлемом времени.

Где Balansis проигрывает

Собрано из тех же прогонов, что и таблицы выше. Измерено 2026-09-08
ЗадачаКто лучшеНасколькоПочему
Точная сумма списка floatmath.fsum25× по времени, равная точностьfsum реализован в C и даёт точный результат на этих входах; Balansis добавляет объекты и Python-цикл
Время скалярного произведения из показанного сценарияnumpy.dot136× по времениNumPy быстрее, но на этих плохо обусловленных входах его ошибка больше; оценивать нужно оба показателя
SVDnumpy_gesdd (то есть сам numpy)105× по времени, ошибка восстановления хуже (5.395e-15 против 2.169e-15)бэкенд act_jacobi реализован на Python поверх dot2; в этом прогоне он не дал преимущества в точности
Деньги с копейкамиdecimal.Decimalточный ноль против ошибки 4.366e-13 на суммах вида 12345.67Ledger.post_entry приводит Decimal к float на входе
Softmaxnumpy со сдвигом на максимум24× по времени, точность та жеBalansis делает тот же сдвиг; отличие — суммирование знаменателя по Кэхэну

Повторить прогон

Для перепроверки не нужен ни доступ к нашей инфраструктуре, ни репозиторий сайта: сам сайт отдаёт скрипт-генератор и полученный им JSON. Для воспроизведения используйте ту же минорную версию Python, что в прогоне (3.12.3), и указанные версии пакетов.

curl -O https://balansis.xteam.pro/repro/generate-benchmarks.py   # тот самый скрипт
curl -O https://balansis.xteam.pro/repro/benchmarks.json     # опубликованные числа

sha256sum generate-benchmarks.py
# 01439b8fd653e7c3a63b09fd86dbecba78c397454aca3f7475b305d90c0c915b

python -m venv .venv && . .venv/bin/activate
python -m pip install "balansis==1.1.0" "numpy==1.26.4"
python generate-benchmarks.py --out mine.json

# сравнить значения; расхождения исследовать вместе с версиями и сборкой NumPy
python - <<'EOF'
import json
mine, theirs = json.load(open("mine.json")), json.load(open("benchmarks.json"))
for a, b in zip(mine["scenarios"], theirs["scenarios"]):
    if a["id"] not in ("aggregation", "dot_product"):
        continue
    for x, y in zip(a["results"], b["results"]):
        print(a["id"], x["method"], x["value"] == y["value"], x["abs_error"], y["abs_error"])
EOF

Скрипт детерминирован по входным данным (все генераторы случайных чисел инициализированы фиксированным seed). Результаты зависят также от версий Python, NumPy, BLAS и условий выполнения арифметики. Расхождения проверяйте по независимому эталону. sha256 скрипта напечатан выше и продублирован в /repro/manifest.json вместе с суммами остальных файлов. Полный JSON — /repro/benchmarks.json, 22 КБ; аудит Lean4 — /repro/lean-audit.json и /repro/generate-lean-audit.py. Короткий вид суммы скрипта: 01439b8fd653e7c3…