Перейти до вмісту

Тестування продуктивності

Інструментарій для тестування продуктивності записує стабільні JSON і звіт для читання. Він міститься в bench/ у репозиторії storh та , його не включали до пакета Composer після 0.5.2, тож наведені нижче команди composer bench* можна виконати лише з клонованого репозиторію після composer install:

Командна оболонка
composer bench
composer bench -- --dataset=100000 --engine=doc
composer bench -- --dataset=100000 --engine=log
composer bench -- --dataset=100000 --engine=queue
composer bench -- --dataset=100000 --engine=cache --cache-validation=trust
composer bench -- --dataset=100000 --engine=filter
composer bench -- --dataset=1000000 --engine=uuid
composer bench:jit -- --dataset=1000000 --engine=uuid
composer bench:repeat -- --dataset=100000 --engine=doc --repeat=5
composer bench:repeat -- --dataset=1000000 --engine=filter --repeat=5 --memory-limit=512M
composer bench:range -- --datasets=1000,10000,50000,100000
composer bench:range -- --datasets=100000 --engines=cache --memory-limit=512M
composer bench:compare build/bench-main.json build/bench-current.json
composer bench:gate -- build/bench-main.json build/bench-current.json --threshold=10 --metric=doc.put --metric=log.stream
composer bench:ci

Еталонні результати

Медіанні значення трьох повних запусків інструментарію, по 50,000 записів на рушій, на Apple M1 Pro (16 GB, macOS 26.5, APFS) із PHP 8.5 CLI, opcache і JIT вимкнено:

Командна оболонка
composer bench:repeat -- --dataset=50000 --engine=all --repeat=3 --memory-limit=512M

Кожен запис скидається на диск і синхронізується через fsync, перш ніж storh повідомляє, що його збережено, тому швидкість запису обмежена файловою системою. Читання виконується з кешу сторінок ОС. Швидкості обчислено за медіанними значеннями; запустіть composer bench із клону репозиторію на власному обладнанні.

DocStore, 50k записів медіана
put(), один довговічний файл на запис 3.3k записів/с
putStream(), масове завантаження 3.7k записів/с
importJsonl() 3.6k записів/с
get(), читання за ключем із перевіркою STAT 5.6 µs
повторне відкриття наявного сховища 0.26 ms
індексований запит на рівність, limit(100) 1.1 ms
індексований count() по всій колекції 41 µs
побудова індексу: 2 поля рівності + 1 діапазону 52k записів/с
повне stream() із повторною перевіркою STAT 103k записів/с
exportJsonl() 104k записів/с
SegmentedLog, 50k записів, сегменти по 16 KB медіана
put(), fsync після кожного додавання 16k додавань/с
appendStream(), масове завантаження 56k записів/с
читання курсором, 100 записів із середини 2.1 ms
читання за діапазоном часу 1.1 ms
запит на рівність count() 11 µs
compact() усіх закритих сегментів 69k записів/с
повторне відкриття з відновленням обрізаного кінця 218k записів/с

Під час тестування сегмент закривається кожні 16 KB, щоб перевірити перемикання між сегментами; розмір сегмента за замовчуванням — 1 MiB.

Queue, 50k завдань медіана
enqueue(), fsync після кожної події 22k завдань/с
claim() 23k завдань/с
complete() 23k завдань/с
enqueueMany() 209k завдань/с
claimMany() 467k завдань/с
completeMany() 512k завдань/с
SQL Mirror, SQLite, 50k записів медіана
початкове push() 61k рядків/с
push() без змін 86k записів/с
flush(), ідентифікатори 100 7.1 ms
індексований SQL COUNT у дзеркалі 6.9 ms
rebuild() 65k рядків/с
відновлення pull(), один довговічний файл на запис 3.2k записів/с
Мікро медіана
кешований get(), холодний, потім теплий (MemoryCache, STAT) 83 µs / 6.3 µs
UUIDv7 генерація 1.3 µs
UUIDv7 перевірка 0.30 µs
фільтрація предикатів у пам’яті 4.4M рядків/с

Охоплені цілі:

  • DocStore: запис, потоковий запис, експорт/імпорт JSONL, отримання, видалення, читання потоком і побудова індексу
  • запити DocStore з індексами рівності, складеними індексами, індексами діапазонів, упорядкованими діапазонами за зростанням і спаданням, підрахунком за індексом, пошуком за ID та без індексів
  • SegmentedLog: додавання, потокове додавання, читання курсором, читання за часовим діапазоном, читання стисненого часового діапазону, пошук курсором і за ID через QueryBuilder, підрахунки, підрахунки пакетного додавання, статистика, стиснення
  • Queue: додавання в чергу, пакетне додавання, отримання, пакетне отримання, завершення, пакетне завершення, повторне додавання
  • фільтрація предикатів RecordQuery і QueryCondition для рядків у пам’яті
  • монотонна генерація UUIDv7, генерація з розподілом часових позначок, перевірка та отримання часових позначок
  • відновлення після обірваного останнього рядка
  • холодне та тепле читання кешу з перевіркою за хешем, статистикою або довіреною перевіркою

Шлях виводу за замовчуванням:

Text
build/bench-current.json

Запуски діапазонів записують один файл JSON для кожного набору даних і рушія в build/bench-ranges/.

bench:gate порівнює два файли бенчмарку JSON і завершується з ненульовим кодом, якщо будь-який відстежуваний показник повільніший за налаштований поріг. Якщо значення --metric не передано, перевіряються всі показники, наявні у файлі базового рівня.

bench:ci — це перевірка регресій у швидкому режимі GitHub Actions. Вона запускає бенчмарк усіх рушіїв на 1k, порівнює вибрані показники запису/читання/кешу/UUID з відстежуваним базовим рівнем bench/baselines/ci-1k-all.json і використовує широкий поріг за замовчуванням, щоб уникнути звичайних коливань середовища запуску, але виявляти значні регресії продуктивності.

bench:jit запускає той самий тестовий комплекс із увімкненими CLI opcache і трасувальним JIT, а для процесу бенчмарку вимикає Xdebug, щоб PHP міг активувати JIT. Згенерований JSON містить прапорці середовища виконання, які показують, чи були активні opcache і JIT під час цього запуску.