Избранное
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 | Техника | Что меряется |
|---|---|---|
| B01 | FFI mmap | таблица на 10M записей: load time, heap, lookup p50/p95/p99, cross-process cold start |
| B02 | SplFixedArray | 10M int-ов: память, заполнение, полный обход, random R/W |
| B03 | Object pool | 5M Point3D аллокаций vs reused pool, GC delta |
| B04 | Lookup-table vs match vs switch | 10M классификаций, три реализации бок о бок |
| B05 | Generator | 5M записей: материализованный массив vs streaming, peak memory |
| B06 | Column- vs row-oriented | 5M записей × 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 и смотрите как цифры дрейфуют со временем.