Mengoptimalkan performa FrankenPHP: aturan praktis num_threads vs memory_limit, tuning opcache dan realpath cache, mengatur GODEBUG/GOMEMLIMIT untuk runtime Go, dan metodologi benchmark yang jujur membandingkan FrankenPHP dengan Nginx + PHP-FPM.

Kita akhirnya sampai pada topik yang paling dicari developer: kinerja. FrankenPHP dikenal dengan klaim "mode worker hingga 3.6x lebih cepat" (perbaikan di 1.12.0, Maret 2026) — tetapi klaim performa tanpa tuning yang benar dan benchmark yang jujur hanyalah iklan. Episode ini membekali kalian dua hal: parameter yang benar untuk diubah, dan metodologi yang benar untuk mengukur.
Mengapa penting? Konfigurasi yang salah bisa membuat FrankenPHP lebih lambat daripada yang seharusnya — contoh klasik: num_threads yang terlalu kecil mengantrekan semua request, atau terlalu besar sehingga memory-limit habis dan worker restart terus. Memahami hubungan parameter-parameter ini membuat perbedaan order-of-magnitude.
num_threads dan Aturan MemoryMode worker membagi request menjadi N thread PHP. Nilai num_threads menentukan paralelisme — terlalu kecil, antrean menumpuk; terlalu besar, memory tercekik.
Aturan praktis resmi dari dokumentasi:
num_threads= total memory / memory limit PHP per request.
{
frankenphp {
num_threads auto
worker {
file /app/public/index.php
num 4
max_consecutive_failures 3
}
}
}
localhost {
root * public/
php_server
}num_threads auto — FrankenPHP menghitung otomatis dari memory_limit PHP (default PHP 128M). Sebaiknya set eksplisit.memory_limit = 512M per request, dan 1GB disisakan untuk base → num_threads = (8 - 1) / 0.5 ≈ 14.Warning
Jika num_threads terlalu besar, sistem kehabisan memori → kernel mematikan proses (OOM) → seluruh worker restart dan semua request hilang. Kurangi num_threads atau naikkan memory_limit. Keduanya saling mengait — jangan utak-atik salah satu saja.
PHP bytecode compilation adalah pekerjaan yang dilakukan terus-menerus; opcache menghilangkannya. Pastikan opcache aktif dan dikonfigurasi dengan benar:
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0Catatan penting:
opcache.validate_timestamps=0 di produksi — mencegah opcache mengecek timestamp file setiap request; kecil tapi ada di hot path. Di development biarkan 1.opcache.jit — PHP 8 menawarkan JIT; untuk aplikasi web, keuntungannya tidak selalu signifikan, tapi bisa dicoba di staging.Realpath cache membantu ketika aplikasi banyak memuat file (framework autoload):
realpath_cache_size=4096K
realpath_cache_ttl=600GODEBUG dan GOMEMLIMITFrankenPHP berjalan di atas runtime Go. Dua env var paling berdampak:
GOMEMLIMIT — batas memori Go (termasuk C heap). Set sejalan dengan memory yang dialokasikan ke server agar Go GC bekerja lebih tenang sebelum OOM.GODEBUG — berbagai flag tuning runtime.GOMEMLIMIT=6GiB
GODEBUG=gctrace=0,madvdontneed=1madvdontneed=1 — kembali ke memori yang dilepas GC lebih cepat ke OS (baik saat memori longgar).gctrace=0 — matikan tracing GC di log (hemat I/O) kecuali kalian sedang debugging.Perilaku memory di container sangat dipengaruhi libc:
GOMEMLIMIT di-set agar Go GC tidak memuncak.Rekomendasi: untuk produksi yang memakai mode worker dengan beban tinggi, pilih image Debian (glibc) — dunglas/frankenphp:1.12.7-php8.5-bookworm — dan set GOMEMLIMIT sesuai num_threads × memory_limit.
Membandingkan FrankenPHP vs Nginx+PHP-FPM gampang dibuat curang. Protokol yang benar:
echo 'hi' tidak mewakili beban framework.Alat yang direkomendasikan (episode 12 menyiapkan metriknya):
hey -z 30s -c 100 https://localhost/ | rg "Requests/sec|p95|p99"Requests/sec: 2450.33
p95: 45.1 ms
p99: 82.7 msGunakan Ember (episode 12) selama benchmark untuk memantau queue depth — jika frankenphp_queue_depth naik terus, num_threads kekecilan.
Ikuti urutan ini daripada menebak:
1. Baseline -> benchmark sebelum menyentuh apa pun
2. opcache -> aktifkan + validate_timestamps=0
3. num_threads -> set sesuai rumus memory
4. GOMEMLIMIT -> sesuai alokasi memory
5. realpath cache -> perbesar untuk framework
6. Benchmark ulang -> bandingkan p95/p99
7. Iterasi -> naik/turun satu parameter, ukur lagiUbah satu variabel per percobaan — mengubah tiga hal sekaligus membuat hasil tidak bisa dikaitkan ke penyebab mana pun.
num_threads auto di produksi: auto cenderung konservatif; set eksplisit untuk mengeksploitasi resource penuh.worker_restarts naik — itu tanda num_threads/memory limit tidak seimbang.Pada episode 17 ini, kalian telah mengoptimalkan performa FrankenPHP dengan benar.
Inti yang harus dibawa pulang:
num_threads = total memory ÷ memory limit per request; seimbangkan, jangan abai.validate_timestamps=0 di produksi; perbesar realpath cache.GOMEMLIMIT mencegah puncak memori Go; madvdontneed=1 untuk release cepat.Di episode 18 selanjutnya kita mendorong transportasi ke batas modern: HTTP/3, QUIC & WebSocket — UDP 443, Early Hints 103, dan real-time via Mercure. Sampai jumpa di episode 18!