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

Производительность

hare-orm против SQLAlchemy, tortoise-orm, yara-orm и Django на PostgreSQL, SQLite и ClickHouse: одни и те же сценарии во всех ORM, написанные так, как их пишет документация каждой ORM, и параллельная нагрузка. У каждой базы своя страница со всеми сценариями.

Сводка

Во сколько раз каждая ORM в среднем медленнее hare-orm на каждой базе: отношение средних геометрических времени по всем сценариям, которые выполняют обе, кроме запуска. hare-orm — это его самый быстрый драйвер на этой базе; прочерк — база, на которой ORM не работает. Последняя строка — операций в секунду у hare-orm под нагрузкой, измеренных «на горячую»: каждое подключение пула выполнило каждую операцию нагрузки до начала замера.

Во сколько раз медленнее hare-orm, по базам Во сколько раз медленнее hare-orm, по базам

По базам

  • PostgreSQL — 50 сценариев: таблица из 1 000 виджетов, у каждого — гаджет, метка и документ JSON.
  • SQLite — 49 сценариев: таблица из 1 000 виджетов, у каждого — гаджет, метка и документ JSON.
  • ClickHouse — 12 сценариев: таблица из 1 000 000 событий, отсортированная по ключу.

Как измерялось

Дата 2026-10-30
Машина Windows 11, 13th Gen Intel(R) Core(TM) i5-13400, 16 CPU
Python 3.14.3
Релиз hare-orm 0.12.0
Прогонов 5, медиана
  • На PostgreSQL у каждой ORM пул из 50 подключений; все они открываются до начала замеров.
  • На PostgreSQL перед каждой группой сценариев и каждым нагрузочным тестом все таблицы вне замера проходят VACUUM (FULL, ANALYZE), а autovacuum для них выключен: каждая группа начинается с компактных таблиц. Иначе мёртвые строки, оставленные записями, убирает autovacuum в случайный момент прогона, и от того, успел ли он, зависит план запроса — с параллельным сканированием или без.
  • Перед нагрузочным тестом каждое подключение пула вне замера выполняет каждую операцию нагрузки: замер показывает работающее приложение, а не только что запущенное, — подключение уже открыто и подготовило свои запросы.
  • Внутри прогона сценарий повторяется 3–5 раз, и в зачёт идёт самый быстрый повтор; строки, которые пишет сценарий записи, удаляются перед каждым повтором вне замера. Удаления и add() многие-ко-многим меняют данные безвозвратно и выполняются один раз.
  • Каждая ORM работает в отдельном процессе. Прогоны идут по кругу, в каждом круге ORM запускаются в случайном порядке; результат сценария — медиана по прогонам.
  • На Windows процесс прогона отключает себе энергосбережение (EcoQoS): иначе планировщик может перенести процесс без окна на энергоэффективные ядра гибридного процессора, где он работает вдвое медленнее.
  • SQLAlchemy на PostgreSQL и SQLite измеряется дважды: с транзакцией вокруг каждой сессии (её поведение по умолчанию) и с движком в режиме autocommit, где каждая команда фиксируется сразу, как в hare и Django.
  • Сценарий, которого в ORM нет, помечен на графике «нет в этой ORM» и не входит в её среднее.
  • Запуск — это инициализация библиотеки и её первый запрос; библиотека к этому моменту уже импортирована. В среднее запуск не входит.
  • На SQLite каждая ORM работает с одним файлом так, как работает по умолчанию; подключения SQLAlchemy ждут блокировку записи файла до 30 с вместо 5 с по умолчанию — иначе нагрузка с записью у её пула из пяти подключений обрывается ошибкой «database is locked». select_for_update() на SQLite не измеряется: блокировок строк в нём нет.
  • Таблицу ClickHouse заполняет сам сервер перед сценариями; мутация (update(), delete()) в каждой библиотеке завершается до возврата из вызова. SQLAlchemy работает через clickhouse-sqlalchemy 0.3.2 с драйвером asynch 0.2.5 на SQLAlchemy 2.0.30 и Python 3.12 — на более новых версиях её асинхронный драйвер не запускается.
  • Каждый сценарий написан так, как его пишет документация этой ORM.

Стенд — benchmarks/bench.py в репозитории; в его README перечислены все сценарии и описано, как повторить замер на своей машине.