Pengguna tidak pernah melihat kode kita; pengguna hanya merasakan jeda antara klik dan hasil yang muncul. Sebelum membicarakan OPcache, Redis, atau optimasi query, chapter ini memastikan kita berbicara bahasa yang sama tentang performa: apa arti “cepat”, siapa yang merasakannya, dan cara berpikir yang membuat setiap perubahan berdampak terukur. Kita akan membedakan latensi dari throughput, mengenali dua karakteristik dasar runtime PHP, dan melatih pola kerja optimasi yang selalu dimulai dan diakhiri dengan pengukuran.
Latensi dan Throughput adalah Dua Ukuran Berbeda #
Latensi (latency) adalah lama waktu satu permintaan diselesaikan dari awal sampai akhir, biasanya dinyatakan dalam milidetik. Throughput adalah jumlah pekerjaan yang diselesaikan per satuan waktu, misalnya permintaan per detik. Keduanya sering disebut bersamaan padahal menjawab pertanyaan yang berbeda:
| Ukuran | Pertanyaan yang dijawab | Satuan umum | Contoh pertanyaan nyata |
|---|---|---|---|
| Latensi | Berapa lama satu permintaan selesai? | milidetik | “Halaman produk terbuka dalam 200 ms?” |
| Throughput | Berapa banyak permintaan terlayani? | permintaan/detik | “Server sanggup melayani 500 permintaan/detik?” |
Hubungan keduanya tidak sederhana: menambah paralelisme (misalnya menambah worker PHP-FPM) menaikkan throughput, tetapi tidak otomatis memperpendek latensi satu permintaan. Selain itu, satu angka “rata-rata” mudah menipu — jika 95 pengguna mendapat 50 ms dan 5 pengguna lain mendapat 5 detik, rata-rata terlihat lumayan padahal sebagian pengguna menderita. Karena itu laporan performa serius memakai persentil seperti p95 atau p99: “95% permintaan selesai di bawah 300 ms”.
Performa yang Dirasakan Pengguna vs yang Dilihat Server #
Performa versi pengguna adalah waktu dari klik sampai halaman benar-benar dapat dipakai: perjalanan jaringan, waktu tanggapan pertama server (TTFB), pengunduhan berkas, parsing HTML, dan rendering di browser. Performa versi server adalah konsumsi sumber daya di dalam kode: waktu CPU, pemakaian memori, dan waktu menunggu I/O seperti query database atau panggilan API.
Keduanya harus diukur karena optimasi server hanya terasa bila menyentuh jalur kritis yang dirasakan pengguna. Mempercepat query yang hasilnya di-cache oleh browser, atau mengoptimalkan fungsi yang berjalan di background jauh dari permintaan, tidak akan mengubah apa pun di mata pengguna. Praktik yang sehat adalah mengukur dari dua arah: waterfall network pada browser DevTools untuk sisi pengguna, serta log dan profil di server untuk sisi kode.
Dua Karakteristik Dasar Runtime PHP #
Shared-Nothing: Semua Dibangun Ulang Setiap Permintaan #
PHP pada SAPI web seperti PHP-FPM mengeksekusi setiap permintaan dari keadaan bersih: superglobal diisi ulang, koneksi database dibuka kembali, dan seluruh hasil perhitungan hilang begitu respons terkirim. Tidak ada objek yang tinggal di memori proses antar permintaan — kecuali yang sengaja disimpan di luar proses, seperti OPcache dan Redis yang kita bahas di chapter-chapter berikutnya. Konsekuensinya, pekerjaan mahal yang sama dapat diulang pada setiap permintaan, dan justru di situlah peluang caching terbesar aplikasi PHP berada.
Waktu Aplikasi Web Didominasi I/O #
Sebagian besar waktu permintaan web PHP bukan dihabiskan CPU untuk berhitung, melainkan untuk menunggu: query database, panggilan API pihak ketiga, serta baca-tulis berkas. Simulasi berikut memperlihatkan cara mengukur versi “menunggu I/O setiap kali” versus versi “hasil sudah tersimpan” tanpa perlu mencetak angka milidetik yang berubah-ubah antar mesin:
<?php
function ambilLaporanLambat(): string
{
usleep(3000); // simulasi menunggu I/O: 3.000 mikrodetik (3 ms)
return "laporan-penjualan-budi";
}
function ambilLaporanCepat(): string
{
// bayangkan hasilnya sudah tersimpan dari pemanggilan sebelumnya
return "laporan-penjualan-budi";
}
$mulai = hrtime(true);
$hasilSebelum = ambilLaporanLambat();
$waktuSebelum = hrtime(true) - $mulai;
$mulai = hrtime(true);
$hasilSesudah = ambilLaporanCepat();
$waktuSesudah = hrtime(true) - $mulai;
echo "Hasil kedua versi identik: ";
var_dump($hasilSebelum === $hasilSesudah);
echo "Waktu versi lama lebih besar: ";
var_dump($waktuSebelum > $waktuSesudah);Output yang Dihasilkan:
Hasil kedua versi identik: bool(true)
Waktu versi lama lebih besar: bool(true)Mari kita telaah apa yang terjadi pada kode di atas:
- Baris 2-6 (
ambilLaporanLambat): fungsi meniru satu panggilan I/O denganusleep(3000)— jeda 3 milidetik yang pasti terjadi setiap dipanggil, lalu mengembalikan sebuah string hasil. - Baris 8-12 (
ambilLaporanCepat): mengembalikan string yang sama tanpa jeda; ini mewakili kondisi setelah perbaikan, misalnya hasil sudah tersimpan di cache. - Baris 14-16 dan 18-20:
hrtime(true)mencatat waktu dalam nanodetik sebelum dan sesudah setiap versi dijalankan (dibedah tuntas pada chapter Profiling PHP). - Baris 22-25: program hanya mencetak fakta struktural — hasil kedua versi identik, dan waktu versi lama lebih besar. Kita sengaja tidak mencetak angka absolut karena nilainya berbeda di setiap mesin, sedangkan urutan besar-kecilnya stabil dan justru itulah yang kita butuhkan.
Loop Optimasi: Ukur, Hipotesis, Ubah, Ukur Lagi #
Optimasi yang bertanggung jawab mengikuti siklus empat langkah yang sama seperti eksperimen ilmiah. Ukur kondisi awal sebagai baseline, bentuk hipotesis tentang letak pemborosan waktu, lakukan satu perubahan yang menyasar hipotesis tersebut, lalu ukur lagi untuk memastikan dampaknya.
Diagram di atas menegaskan dua syarat yang sering dilupakan: perubahan harus membuktikan dirinya lewat pengukuran ulang, dan hasil program tidak boleh berubah hanya karena kita mempercepatnya — pada simulasi sebelumnya, pemeriksaan $hasilSebelum === $hasilSesudah menjaga syarat kedua itu.
Optimasi Prematur dan Hukum Amdahl #
Tidak semua kode layak dioptimalkan. Optimasi prematur terjadi ketika kita memperhalus kode yang jarang dijalankan sementara biaya sesungguhnya terletak di tempat lain — misalnya merapikan perulangan mikro ketika 80% waktu permintaan habis untuk satu query yang tidak memakai indeks. Alat untuk menemukan letak biaya tersebut adalah profiling, yang kita bahas pada chapter berikutnya.
Hukum Amdahl memberi intuisi matematisnya: jika porsi p dari total waktu dipercepat sebesar faktor s, percepatan keseluruhan dibatasi oleh porsi sisanya yang tidak disentuh — 1 / ((1 - p) + p / s). Tabel berikut memperlihatkan batas tersebut:
| Porsi waktu yang dipercepat | Perbaikan 2× | Perbaikan 5× | Perbaikan tanpa batas |
|---|---|---|---|
| 10% | 1.05× | 1.09× | 1.11× |
| 25% | 1.14× | 1.25× | 1.33× |
| 50% | 1.33× | 1.67× | 2.00× |
| 75% | 1.60× | 2.50× | 4.00× |
Perhatikan kolom terakhir: mempercepat 75% bagian sebesar apa pun pun hanya menghasilkan aplikasi 4× lebih cepat, karena 25% sisanya tetap membayar harga lamanya. Kode berikut menghitung batas percepatan maksimal (perbaikan tanpa batas) untuk beberapa porsi:
<?php
function batasPercepatanMaksimal(float $porsiYangDipercepat): float
{
return 1.0 / (1.0 - $porsiYangDipercepat);
}
foreach ([0.10, 0.25, 0.50, 0.75] as $porsi) {
printf(
"Porsi yang dipercepat %2d%%: percepatan maksimal %.2fx\n",
(int) round($porsi * 100),
batasPercepatanMaksimal($porsi)
);
}Output yang Dihasilkan:
Porsi yang dipercepat 10%: percepatan maksimal 1.11x
Porsi yang dipercepat 25%: percepatan maksimal 1.33x
Porsi yang dipercepat 50%: percepatan maksimal 2.00x
Porsi yang dipercepat 75%: percepatan maksimal 4.00xFungsi batasPercepatanMaksimal() pada baris 2-5 menghitung rumus Amdahl untuk kasus s menuju tak terhingga: yang tersisa hanyalah 1 / (1 - p). Perulangan pada baris 7-13 mencetak batas tersebut untuk porsi 10%, 25%, 50%, dan 75% — angka-angka ini persis mengisi kolom terakhir tabel di atas.
Ringkasan #
Performa punya dua wajah: latensi yang dirasakan per permintaan dan throughput yang menunjukkan kapasitas layanan; keduanya diukur dari sisi pengguna maupun sisi server. Runtime PHP membangun ulang semuanya setiap permintaan dan menghabiskan sebagian besar waktu untuk menunggu I/O, sehingga caching menjadi senjata utama. Loop optimasi mengharuskan kita mengukur sebelum dan sesudah berubah, dan Hukum Amdahl mengingatkan bahwa percepatan selalu dibatasi bagian yang tidak disentuh. Langkah pertama dari loop tersebut — mengetahui ke mana waktu pergi — adalah materi chapter Profiling PHP berikutnya.
Referensi #
- PHP: The Right Way — praktik umum komunitas, termasuk budaya ukur-dulu-baru-optimalkan.
- PHP: Supported Versions — mengapa tetap berada di versi PHP yang didukung juga merupakan keputusan performa.