Optimasi tanpa pengukuran adalah menebak — dan tebakan kadang merugikan: kode yang “terasa” lebih canggih bisa ternyata lebih lambat. Sebelum mengubah satu baris pun, kita perlu tahu apa yang diukur, bagaimana kondisinya disamakan, dan bukti apa yang dianggap berhasil.
Chapter penutup section ini menyusun semua alat yang sudah kita pelajari — stopwatch, penghitung operasi, pola fragment — menjadi protokol pengukuran sebelum–sesudah, lalu menerapkannya pada dua studi kasus yang datanya nyata dan teruji: loop penyambungan string dan pencarian pada 10.000 item.
Metrik yang Layak Dipantau #
Metrik adalah besaran yang menjawab “baik itu berapa?”. Tanpa metrik yang disepakati, setiap orang menilai dari rasa. Beberapa metrik yang lazim untuk halaman web:
| Metrik | Menjawab pertanyaan | Cara mengamati |
|---|---|---|
| Waktu muat | Kapan konten utama sudah tampil? | DevTools → tab Network/Performance |
| Time to Interactive (TTI) | Kapan halaman merespons interaksi dengan layak? | Lighthouse / rekaman Performance |
| Durasi operasi kunci | Berapa lama render daftar, pencarian, atau filter? | performance.now(), console.time |
Dua istilah pertama bekerja di tingkat halaman. Waktu muat dibaca dari catatan waktu navigasi yang mencatat browser sendiri — kapan HTML selesai, kapan konten utama tampil. TTI adalah konsep untuk titik ketika halaman tidak hanya tampil, tetapi juga siap melayani interaksi: halaman yang menampilkan semuanya namun main thread-nya sibuk memproses script membuat klik terasa tertahan, dan TTI yang buruk itulah gejalanya. Penyebab teknisnya juga sudah kita kenal dari chapter Profiling: tugas panjang di main thread menahan callback interaksi sampai tugas itu selesai. Keduanya diamati lewat DevTools atau Lighthouse, bukan dihitung dari script kita.
Metrik ketiga adalah domain kita sepanjang section ini: durasi operasi yang kita tulis sendiri, diukur dengan performance.now() dan console.time dari chapter Profiling.
Protokol Sebelum–Sesudah #
Pengukuran yang sah mengikuti lima langkah yang disiplin:
- Tetapkan baseline. Ukur kondisi awal tanpa perubahan apa pun, dengan data dan mesin yang sama.
- Kunci kebenaran hasil dulu. Optimasi yang mengubah hasil kerja kode adalah bug — pastikan versi baru menghasilkan keluaran yang identik.
- Ubah satu hal saja. Dua perubahan sekaligus membuat sumber selisihnya tidak bisa dipastikan.
- Ukur ulang dengan kondisi yang sama.
- Bandingkan rasio, ulangi beberapa kali. Satu run bukan bukti; kesimpulan sah datang dari selisih yang konsisten.
Dua catatan kerja melengkapi protokol. Pertama, tuliskan hasil pengukuran — angka yang hanya ada di kepala berubah-ubah setiap kali diingat; catatan singkat (kondisi, tanggal, hasil, kesimpulan) membuat keputusan bisa ditelusuri ulang. Kedua, jaga setiap run tetap mudah diulang: bila pengukuran baseline membutuhkan langkah manual yang panjang, buat berkas pengujian kecil yang menjalankannya — investasi sekali, dipakai setiap kali perubahan berikutnya datang.
Langkah kedua sering dilewati dan justru yang paling murah dicek. Studi kasus berikut mempraktikkan urutan itu: kebenaran hasil dibuktikan dengan angka yang deterministik, baru durasi dibandingkan dengan catatan yang jujur.
Studi Kasus: Loop dan Pencarian pada 10.000 Item #
Dua kandidat optimasi yang klasik: penyambungan string dengan += di loop versus join(), dan pencarian dengan includes pada array versus has pada Set. Keduanya kita uji pada data yang sama — dan sebelum bicara waktu, keluarannya diperiksa dulu:
// uji-optimasi.js — memastikan versi "cepat" memberi hasil yang sama dulu
// --- Eksperimen 1: penyambungan string, loop += vs join ---
const kata = [];
for (let i = 0; i < 5000; i++) {
kata.push("kata" + i);
}
let viaConcat = "";
for (const k of kata) {
viaConcat += k;
}
const viaJoin = kata.join("");
console.log("hasil sama :", viaConcat === viaJoin);
console.log("panjang teks :", viaConcat.length);
// --- Eksperimen 2: pencarian pada 10.000 angka ---
const daftar = [];
for (let i = 0; i < 10000; i++) {
daftar.push(i);
}
const himpunan = new Set(daftar);
const yangDicari = [0, 2500, 5000, 7500, 9999, -1];
let ketemuArray = 0;
for (const dicari of yangDicari) {
if (daftar.includes(dicari)) ketemuArray++;
}
let ketemuSet = 0;
for (const dicari of yangDicari) {
if (himpunan.has(dicari)) ketemuSet++;
}
console.log("array includes :", ketemuArray, "dari", yangDicari.length);
console.log("set has :", ketemuSet, "dari", yangDicari.length);
console.log("angka -1 di set :", himpunan.has(-1));
console.log("isi set :", Array.from(himpunan).length, "item");Menjalankan node uji-optimasi.js menghasilkan:
Output yang Dihasilkan:
hasil sama : true
panjang teks : 38890
array includes : 5 dari 6
set has : 5 dari 6
angka -1 di set : false
isi set : 10000 itemMari kita telaah baris-baris pentingnya:
- Baris 9–12 (versi
+=): string disambung satu per satu di dalam loop. Setiap+=menghasilkan string baru yang isinya seluruh teks sebelumnya plus satu potongan — pekerjaan yang tumbuh mengikuti panjang teks. - Baris 13–14 (verifikasi kesejajaran):
viaConcat === viaJoinmenjawabtruedan panjangnya38890karakter — inilah langkah 2 dari protokol: kedua versi sah karena hasilnya identik. Angka-angka ini tetap sama di mesin apa pun. - Baris 22 (
new Set(daftar)):Setdibangun sekali dari array 10.000 angka. Biaya pembangunannya dibayar satu kali, laluhasbekerja pada struktur yang dirancang untuk pencarian — bukan menyisir elemen satu per satu sepertiincludes. - Baris 25–32 (dua penghitung jawaban): keenam nilai yang dicari memuat lima angka yang ada dan satu angka yang tidak ada (
-1). Kedua versi menjawab5 dari 6— persis sama termasuk soal kasus negatifnya.Array.from(himpunan).lengthmengembalikan10000, membuktikan tidak ada elemen yang hilang saat dipindah keSet. - Baris 33–36 (pelaporan hasil): keluaran menyajikan jumlah jawaban, bukan durasi — sesuai langkah 2 protokol: kebenaran dulu, kecepatan kemudian.
Sekarang bagian durasi. Satu pencarian enam nilai terlalu singkat untuk diukur stabil, maka pekerjaannya diperbesar: seribu putaran pencarian, seperti pola yang ditekankan di chapter Profiling:
const tMulai = performance.now();
for (let putaran = 0; putaran < 1000; putaran++) {
for (const dicari of yangDicari) {
if (himpunan.has(dicari)) ketemuSet++;
}
}
const tSelesai = performance.now();
console.log("set has:", (tSelesai - tMulai).toFixed(2), "ms");Contoh hasil di mesin penulis (Node v24.21.0) — angka Anda pasti berbeda:
| Pengukuran | Sebelum | Sesudah | Rasio kasar |
|---|---|---|---|
| Penyambungan 5.000 kata | += ± 0,25 ms | join() ± 0,08 ms | ± 3× |
| 6.000 pencarian di 10.000 item | includes ± 32 ms | Set.has ± 0,4 ms | ± 80× |
Pembacaan tabelnya sesuai protokol: selisih pada pencarian berulang ribuan kali — puluhan milidetik melawan sepersekian milidetik — jauh melampaui variasi antar-run, sehingga kesimpulannya kokoh. Untuk penyambungan string, selisihnya ada tetapi kecil pada skala ini; pada data jauh lebih besar, join() semakin unggul karena runtime bisa menghitung total panjang lebih dulu. Tanpa pengukuran, dua fakta berbeda semacam ini mustahil dipisahkan dari perasaan.
Dua studi kasus ini menutup section Performance dengan pola kerja yang utuh: tetapkan metrik, ukur baseline, pastikan hasil tetap benar, ubah satu hal, ukur ulang, lalu simpulkan dari rasio — bukan dari rasa. Kebiasaan inilah yang membedakan pengoptimalan dari kebetulan, dan fondasinya akan terus terpakai saat kita menyusun aplikasi berskala besar di section Architecture dan Patterns.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
uji-optimasi.js
Unduh berkas// uji-optimasi.js — memastikan versi "cepat" memberi hasil yang sama dulu
// --- Eksperimen 1: penyambungan string, loop += vs join ---
const kata = [];
for (let i = 0; i < 5000; i++) {
kata.push("kata" + i);
}
let viaConcat = "";
for (const k of kata) {
viaConcat += k;
}
const viaJoin = kata.join("");
console.log("hasil sama :", viaConcat === viaJoin);
console.log("panjang teks :", viaConcat.length);
// --- Eksperimen 2: pencarian pada 10.000 angka ---
const daftar = [];
for (let i = 0; i < 10000; i++) {
daftar.push(i);
}
const himpunan = new Set(daftar);
const yangDicari = [0, 2500, 5000, 7500, 9999, -1];
let ketemuArray = 0;
for (const dicari of yangDicari) {
if (daftar.includes(dicari)) ketemuArray++;
}
let ketemuSet = 0;
for (const dicari of yangDicari) {
if (himpunan.has(dicari)) ketemuSet++;
}
console.log("array includes :", ketemuArray, "dari", yangDicari.length);
console.log("set has :", ketemuSet, "dari", yangDicari.length);
console.log("angka -1 di set :", himpunan.has(-1));
console.log("isi set :", Array.from(himpunan).length, "item");