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:

MetrikMenjawab pertanyaanCara mengamati
Waktu muatKapan konten utama sudah tampil?DevTools → tab Network/Performance
Time to Interactive (TTI)Kapan halaman merespons interaksi dengan layak?Lighthouse / rekaman Performance
Durasi operasi kunciBerapa 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:

  1. Tetapkan baseline. Ukur kondisi awal tanpa perubahan apa pun, dengan data dan mesin yang sama.
  2. Kunci kebenaran hasil dulu. Optimasi yang mengubah hasil kerja kode adalah bug — pastikan versi baru menghasilkan keluaran yang identik.
  3. Ubah satu hal saja. Dua perubahan sekaligus membuat sumber selisihnya tidak bisa dipastikan.
  4. Ukur ulang dengan kondisi yang sama.
  5. 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:

text
hasil sama      : true
panjang teks    : 38890
array includes  : 5 dari 6
set has         : 5 dari 6
angka -1 di set : false
isi set         : 10000 item

Mari kita telaah baris-baris pentingnya:

  1. 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.
  2. Baris 13–14 (verifikasi kesejajaran): viaConcat === viaJoin menjawab true dan panjangnya 38890 karakter — inilah langkah 2 dari protokol: kedua versi sah karena hasilnya identik. Angka-angka ini tetap sama di mesin apa pun.
  3. Baris 22 (new Set(daftar)): Set dibangun sekali dari array 10.000 angka. Biaya pembangunannya dibayar satu kali, lalu has bekerja pada struktur yang dirancang untuk pencarian — bukan menyisir elemen satu per satu seperti includes.
  4. Baris 25–32 (dua penghitung jawaban): keenam nilai yang dicari memuat lima angka yang ada dan satu angka yang tidak ada (-1). Kedua versi menjawab 5 dari 6 — persis sama termasuk soal kasus negatifnya. Array.from(himpunan).length mengembalikan 10000, membuktikan tidak ada elemen yang hilang saat dipindah ke Set.
  5. 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:

js Pola pengukuran: perbesar pekerjaan agar durasinya terukur
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:

PengukuranSebelumSesudahRasio kasar
Penyambungan 5.000 kata+= ± 0,25 msjoin() ± 0,08 ms± 3×
6.000 pencarian di 10.000 itemincludes ± 32 msSet.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
javascript
// 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");