Skip to main content

Избранное

php-llamacpp-benchmarks

Шесть llama.cpp-вдохновлённых паттернов оптимизации, переведённых в идиоматичный PHP 8.4 с JIT, плюс naive-vs-optimised importer CSV → Postgres. Воспроизводится через Docker + Make.

  • PHP 8.4
  • llama.cpp
  • FFI
  • JIT
  • Docker
  • PostgreSQL 16
  • PHPStan L8

Зачем это

llama.cpp — один из самых агрессивно оптимизированных C/C++-кодбейзов в активной разработке. Mmap-веса, плоские плотные буферы, value pools, table dispatch, token streaming, columnar layouts — каждая cache-line и каждая аллокация на счету. Большинство этих приёмов в теории языко-агностичные. Возникает честный вопрос: сколько из этого выживет в PHP 8.4 с JIT и что каждый паттерн реально даёт?

Этот репозиторий отвечает цифрами. Шесть микро-бенчмарков плюс один реалистичный case study, всё воспроизводимо через docker compose и Makefile. Сопровождающая статья на Medium — I Scaled PHP Until It Broke. Three llama.cpp Patterns Saved It. — разбирает что сработало и что нет.

Что входит в сьют

IDТехникаЧто меряется
B01FFI mmapтаблица на 10M записей: load time, heap, lookup p50/p95/p99, cross-process cold start
B02SplFixedArray10M int-ов: память, заполнение, полный обход, random R/W
B03Object pool5M Point3D аллокаций vs reused pool, GC delta
B04Lookup-table vs match vs switch10M классификаций, три реализации бок о бок
B05Generator5M записей: материализованный массив vs streaming, peak memory
B06Column- vs row-oriented5M записей × 5 полей: single-column scan + full-row scan

Каждый прогон — warmup → measured iterations → hrtime(true) → memory_get_peak_usage(true) → percentiles через линейную интерполяцию. Выигрыш 1.2× показан как 1.2×. Без кулинарии.

Сквозной case study

bin/run-case-study.php гоняет два importer-а на одном CSV в 100K строк и одной Postgres-таблице:

  • Naive importer — полный CSV в массив, new Record() на каждую строку, assoc-array dedupe, JSON-загруженная мапа стран, single-row INSERT-ы.
  • Optimised importer — Generator CSV reader (B05), pooled Record (B03), SplFixedArray-backed dedupe с linear probing (B02), mmap-овая бинарная таблица стран (B01), multi-VALUES INSERT по 1000 строк.

Шесть паттернов, одна реалистичная нагрузка. Смысл не в одной геройской цифре — а в том чтобы увидеть, какой именно паттерн себя окупает, а какой избыточен.

Honesty notes

  • Бенчмарки крутятся внутри Docker (php:8.4-cli-bookworm, opcache + JIT). Прогон 2–3 раза — первый замер всегда шумнее.
  • B01 меряет warm load time (kernel page cache прогрет). Реальные cold-disk цифры зависят от стораджа и за scope in-process бенчмарка.
  • Выигрыш B03 на one-shot CLI обычно скромный. Паттерн себя показывает в long-running воркерах (очереди, websocket-ы).
  • B04 может дать меньший выигрыш чем диктует C-интуиция: JIT PHP 8.3 компилирует match очень хорошо. Статья это разбирает.

Что получаете при клонировании

make build        # собрать php-образ (один раз)
make all          # install, fixtures, db, bench, case-study

На выходе: results/results.md и results/results.json с p50/p95/p99 на каждый бенчмарк, peak memory и hash коммита. Прицепляете к CI и смотрите как цифры дрейфуют со временем.

По теме