Teknologi

Kenapa CPU 100% Tapi Query Lambat? Latency Memori, Bukan Kurang Core

Kenapa CPU 100% Tapi Query Lambat? Latency Memori, Bukan Kurang Core

CPU yang menunjukkan 100% di dashboard belum tentu benar-benar sibuk. Sebuah CPU modern bisa memproses sekitar 4 sampai 8 instruksi per nanusekon, sementara satu akses ke RAM butuh 50 sampai 100 nanusekon. Artinya CPU bisa menunggu setara 400 sampai 800 instruksi hanya untuk satu kali ambil data dari memori. Ketika sebuah query lambat padahal CPU terlihat penuh, penyebabnya sering bukan kurangnya daya komputasi, tapi latency memori yang tidak bisa disembunyikan. Pivot menerbitkan analisis ini pada 28 Oktober 2026 dengan studi kasus mempercepat GROUP BY.

TL;DR

  • DRAM butuh 50 sampai 100 nanusekon per akses, setara 400 sampai 800 instruksi CPU pada CPU yang bisa mengeksekusi 4 hingga 8 instruksi per nanusekon.
  • L1 cache sekitar 1 nanusekon dan berukuran sekitar 64 KB, jadi 50 sampai 100 kali lebih cepat daripada RAM.
  • L3 cache sekitar 10 sampai 15 nanusekon dengan kapasitas sampai beberapa ratus MB, masih jauh lebih lambat dari L1 dan L2.
  • Angka 100% pada monitoring CPU hanya berarti ada task yang butuh CPU time, bukan berarti unit eksekusi benar-benar terpakai penuh.
  • Strategi mempercepat GROUP BY di studi kasus itu justru menambah kerja CPU supaya akses memori jadi lebih sedikit dan lebih terprediksi.

Kenapa CPU 100% tidak berarti CPU sibuk?

Karena metrik CPU di sistem operasi mengukur ada tidaknya task yang meminta waktu CPU, bukan seberapa banyak unit eksekusi di dalam chip yang benar-benar bekerja. CPU dianggap idle hanya kalau tidak ada task yang membutuhkan CPU time. Ketika sebuah task berjalan tapi sebagian besar waktunya habis menunggu data dari memori, sistem tetap melaporkannya sebagai pemakaian CPU, sehingga angka 100% muncul meski throughput instruksinya rendah.

Ini menjelaskan gejala yang sering membingungkan saat menuning query: server terlihat jenuh, tapi menambah core tidak memperbaiki apa pun. Kalau hambatannya adalah latency memori, menambah core hanya menambah jumlah pekerja yang sama-sama menunggu. Yang berubah adalah antreannya, bukan durasi tunggunya. Kesimpulan praktisnya, sebelum menambah resource, pastikan dulu apakah waktu benar-benar habis di komputasi atau di penantian data.

Seberapa jauh jarak kecepatan antar level cache?

Jaraknya sangat lebar dan bertingkat, dan itulah alasan hierarki cache ada. Angka berikut diambil dari tabel di analisis Pivot, dengan konversi kasar berapa banyak instruksi yang bisa dieksekusi CPU selama satu akses memori berlangsung.

LevelLatencyKapasitas tipikalSetara instruksi
Register dan eksekusi CPUkurang dari 1 nspuluhan register4 sampai 8 instruksi per ns
L1 cachesekitar 1 nssekitar 64 KB4 sampai 8 instruksi
L2 cache3 sampai 4 nssekitar 1 MB15 sampai 30 instruksi
L3 cache10 sampai 15 nssampai beberapa ratus MB50 sampai 100 instruksi
DRAM50 sampai 100 nssatuan GB400 sampai 800 instruksi

L1 berada di kisaran 1 nanusekon per operasi, jadi sekitar 50 sampai 100 kali lebih cepat daripada RAM. Masalahnya, L1 hanya sekitar 64 KB, sedangkan cache paling jauh di prosesor kelas atas mentok di beberapa ratus MB. Perbedaan kapasitas itulah yang memaksa setiap program memilih: menyimpan data sedekat mungkin ke CPU dengan ruang terbatas, atau menyimpan semuanya di RAM dengan latency yang mahal.

Apa hubungannya dengan cara database mengeksekusi query?

Sangat langsung, karena database yang baik sengaja memanipulasi data dalam potongan kecil supaya tetap muat di cache. Istilah yang sering muncul adalah cache utilization: seberapa besar proporsi akses yang berhasil dilayani dari cache, bukan dari RAM. Query yang membaca baris satu per satu dengan pola acak akan sering meleset dari cache, sedangkan query yang membaca blok berurutan bisa memanfaatkan prefetch dan menjaga data tetap dekat ke CPU.

Di sinilah trade-off yang sering salah dipahami muncul. Menambah kerja CPU bisa mempercepat keseluruhan proses kalau kerja tambahan itu menggantikan akses memori yang mahal. Menghitung ulang sesuatu yang murah di CPU lebih baik daripada mengambilnya dari struktur data besar yang tersebar di RAM. Prinsip ini yang dipakai Pivot untuk mempercepat GROUP BY: alih-alih mengandalkan struktur hash besar yang aksesnya acak, mereka mengubah sebagian beban menjadi operasi yang lebih ramah cache.

Kapan strategi "tambah kerja CPU" masuk akal?

Masuk akal ketika waktu dominan habis di penantian memori, bukan di komputasi. Tanda-tandanya bisa dibaca dari perilaku sistem: CPU terlihat penuh tapi throughput datar, penambahan core tidak menaikkan hasil, dan waktu eksekusi melonjak ketika ukuran data melewati batas kapasitas cache tertentu. Kalau salah satu dari pola itu muncul, biaya akses memori patut dicurigai sebagai penyebab utama.

Sebaliknya, strategi ini tidak masuk akal kalau CPU sudah benar-benar menjadi hambatan. Menambah komputasi pada sistem yang sudah compute-bound hanya memindahkan antrean. Yang perlu dilakukan di kondisi itu adalah mengurangi jumlah pekerjaan, bukan mengubah bentuknya. Karena itu, pengukuran awal bukan langkah opsional, melainkan penentu arah optimasi.

perf stat -e cache-misses,cache-references,instructions,cycles ./program
perf stat -e LLC-load-misses,LLC-loads ./program

Dua perintah itu memberi gambaran kasar berapa banyak akses yang meleset dari cache relatif terhadap total akses. Kalau rasio miss tinggi dan cycles per instruksi ikut naik, hipotesis latency memori masuk akal. Kalau rasio miss rendah tapi cycles tetap tinggi, hambatannya ada di tempat lain, misalnya percabangan yang sulit diprediksi.

Apakah ini relevan untuk server dengan RAM besar?

Relevan, dan justru sering jadi jebakan. RAM besar membuat orang berasumsi dataset akan tinggal di memori sehingga aksesnya cepat. Yang terlupakan: RAM besar tidak mempercepat akses ke RAM, ia hanya memperbesar kapasitas. Latency DRAM tetap 50 sampai 100 nanusekon. Dataset yang muat di RAM memang tidak perlu membaca disk, tapi setiap aksesnya tetap membayar biaya yang sama besarnya.

Karena itu, di server dengan RAM melimpah pun, struktur data yang buruk tetap lambat. Yang berubah hanya sumber penantiannya: dari disk ke RAM. Selama CPU masih menghabiskan waktu menunggu data, menambah RAM tidak akan mengubah throughput. Yang mengubahnya adalah memperbaiki lokalitas akses supaya porsi yang dilayani L1, L2, dan L3 meningkat.

Bagaimana menerjemahkan ini jadi keputusan praktis?

Mulai dari mengukur, bukan menebak. Ambil satu query yang lambat, catat waktu eksekusinya, lalu lihat rasio cache miss dan cycles per instruksi. Setelah itu baru putuskan apakah masalahnya volume data, pola akses, atau memang kurang daya komputasi. Tanpa urutan itu, optimasi cenderung menjadi percobaan acak yang menghabiskan waktu tapi tidak mengubah angka.

Langkah berikutnya, uji perubahan pada ukuran data yang berbeda-beda. Query yang cepat pada 100 ribu baris bisa mendadak lambat pada 10 juta baris bukan karena algoritmanya salah, tapi karena datanya tidak lagi muat di cache. Kalau titik patahnya konsisten dengan batas kapasitas cache, penyebabnya hampir pasti pola akses memori. Pengujian pada satu ukuran data saja akan menyembunyikan informasi itu.

Bagaimana mengenali titik patah akibat kapasitas cache?

Caranya dengan mengukur pada beberapa ukuran data, bukan satu. Ukur query yang sama pada 100 ribu, 1 juta, dan 10 juta baris, lalu gambar waktu eksekusinya. Kalau kurvanya naik landai lalu mendadak menanjak di satu titik, titik itu biasanya bertepatan dengan batas kapasitas cache. Lonjakan mendadak seperti itu tidak dijelaskan oleh pertumbuhan linear, sehingga penyebabnya bukan volume data semata.

Pola kedua yang perlu dicurigai adalah throughput yang datar meski CPU dilaporkan penuh. Tambah satu core, hasilnya tidak berubah. Hapus satu kolom yang tidak dipakai, hasilnya membaik. Gejala seperti itu menunjukkan bahwa pekerjaan sebenarnya bukan menghitung, melainkan menunggu. Dalam kondisi itu, memangkas jumlah data yang dibaca lebih berdampak daripada mengoptimalkan ekspresi aritmatika.

Pola ketiga, waktu eksekusi yang sangat sensitif terhadap urutan baris masukan. Kalau mengubah urutan data membuat query jauh lebih cepat tanpa mengubah hasil, berarti lokalitas akses yang berubah, bukan algoritmanya. Sensitivitas terhadap urutan adalah salah satu petunjuk paling kuat bahwa biaya akses memori mendominasi. Query yang benar-benar compute-bound biasanya tidak peduli urutan masukannya.

Yang perlu dihindari justru mengoptimalkan berdasarkan intuisi nama teknik. Teknik yang terdengar canggih tidak otomatis cocok untuk hierarki cache tertentu, dan teknik sederhana bisa jauh lebih cepat kalau pola aksesnya berurutan. Ukur dulu, baru pilih. Urutan sebaliknya menghasilkan kode yang sulit dibaca tanpa perbaikan performa yang bisa dibuktikan.

Sumber dan bacaan lanjut

  • Analisis asli berjudul "It's the memory latency, stupid" di blog Pivot, terbit 28 Oktober 2026, sumber seluruh angka latency di artikel ini.
  • Kumpulan manual optimasi perangkat lunak yang jadi rujukan klasik untuk perilaku CPU dan cache: agner.org/optimize.
  • Dokumentasi perencana query PostgreSQL di postgresql.org, tempat biaya baca halaman dipakai sebagai basis keputusan.
  • Dokumentasi kernel Linux di docs.kernel.org untuk pengaturan NUMA dan manajemen memori.

FAQ

Apakah angka 4 sampai 8 instruksi per nanusekon berlaku untuk semua CPU?

Tidak, itu kisaran kasar untuk CPU modern yang dipakai dalam analisis tersebut. Angka pastinya berbeda antararsitektur dan bergantung pada jenis instruksi. Yang tetap berlaku adalah rasionya: satu akses DRAM membayar waktu yang setara ratusan instruksi, dan selisih itu tidak berubah hanya karena CPU-nya lebih baru.

Kenapa L3 bisa sampai beberapa ratus MB?

Karena prosesor kelas atas menambah kapasitas cache untuk menutup jarak antara L2 yang kecil dan RAM yang lambat. Konsekuensinya, latency L3 juga naik dibanding L2, yaitu sekitar 10 sampai 15 nanusekon. Jadi cache yang lebih besar bukan berarti lebih cepat per akses, hanya lebih banyak data yang bisa ditahan dekat CPU.

Apakah GROUP BY selalu lebih baik dihitung ulang daripada memakai hash?

Tidak selalu. Kesimpulan itu berlaku untuk kasus yang diuji, di mana struktur hash besar membuat akses memori tersebar dan mahal. Kalau jumlah grupnya sedikit dan datanya muat di cache, pendekatan hash bisa tetap lebih cepat. Yang menentukan adalah ukuran kerja dan lokalitas aksesnya, bukan nama tekniknya.

Alat apa yang paling sederhana untuk memulai?

perf stat sudah cukup untuk melihat rasio cache miss dan cycles per instruksi pada Linux. Kalau butuh lebih detail per fungsi, perf record dan perf report memberi profil per simbol. Untuk perbandingan cepat antarversi query, cukup ukur waktu eksekusi pada beberapa ukuran data dan lihat titik patahnya.

Apakah latency memori bisa diperbaiki lewat konfigurasi sistem?

Sebagian kecil, misalnya lewat pengaturan NUMA supaya proses mengambil memori dari node yang dekat. Tapi latency dasar DRAM tidak berubah oleh konfigurasi. Perbaikan yang berdampak besar hampir selalu datang dari perubahan pola akses data, bukan dari parameter sistem. Itu sebabnya memahami hierarki cache lebih berguna daripada menuning kernel.

💬 Komentar (0)

Belum ada komentar. Jadilah yang pertama! 💬

Komentar yang memuat tautan, bahasa asing, atau aksara non-Latin ditahan dulu buat ditinjau.