Jawaban ini bukanlah sebuah jawaban, tetapi lebih dari kumpulan catatan.
Pertama, CPU cenderung beroperasi pada jalur cache, bukan pada byte/kata/kata individual. Ini berarti bahwa jika Anda secara berurutan membaca/menulis array bilangan bulat maka akses pertama ke baris cache dapat menyebabkan cache hilang tetapi akses selanjutnya ke bilangan bulat berbeda di baris cache yang sama tidak akan terjadi. Untuk baris cache 64-byte dan bilangan bulat 4-byte, ini berarti Anda hanya akan mendapatkan cache yang hilang satu kali untuk setiap 16 akses; yang akan mencairkan hasilnya.
Kedua, CPU memiliki "pre-fetcher perangkat keras". Jika mendeteksi bahwa baris cache sedang dibaca secara berurutan, pre-fetcher perangkat keras akan secara otomatis mengambil baris cache yang diperkirakan akan diperlukan berikutnya (dalam upaya untuk mengambilnya ke dalam cache sebelum diperlukan).
Ketiga, CPU melakukan hal lain (seperti "out of order execution") untuk menyembunyikan biaya pengambilan. Perbedaan waktu (antara cache hit dan cache miss) yang dapat Anda ukur adalah waktu yang tidak dapat disembunyikan oleh CPU dan bukan total biaya pengambilan.
Gabungan 3 hal ini berarti; untuk membaca larik bilangan bulat secara berurutan, kemungkinan CPU melakukan pra-pengambilan baris cache berikutnya saat Anda melakukan 16 kali pembacaan dari baris cache sebelumnya; dan setiap biaya kehilangan cache tidak akan terlihat dan mungkin sepenuhnya tersembunyi. Untuk mencegah hal ini; Anda ingin "secara acak" mengakses setiap baris cache satu kali, untuk memaksimalkan perbedaan performa yang diukur antara "set kerja sesuai dengan cache" dan "set kerja tidak sesuai dengan cache".
Akhirnya, ada faktor lain yang dapat mempengaruhi pengukuran. Misalnya, untuk OS yang menggunakan paging (mis. Linux dan hampir semua OS modern lainnya) ada seluruh lapisan caching di atas semua ini (TLBs/Translation Look-aside Buffers), dan TLB meleset begitu perangkat kerja melampaui ukuran tertentu; yang harus terlihat sebagai "langkah" keempat dalam grafik. Ada juga gangguan dari kernel (IRQ, kesalahan halaman, sakelar tugas, banyak CPU, dll); yang mungkin terlihat sebagai statis/kesalahan acak dalam grafik (kecuali pengujian sering diulang dan outlier dibuang). Ada juga artefak desain cache (asosiasi cache) yang dapat mengurangi keefektifan cache dengan cara yang bergantung pada alamat fisik yang dialokasikan oleh kernel; yang mungkin terlihat sebagai "langkah" dalam grafik yang bergeser ke tempat yang berbeda.
Apakah ada yang salah dengan metode saya?
Mungkin, tetapi tanpa melihat kode Anda yang sebenarnya yang tidak dapat dijawab.
-
Deskripsi Anda tentang apa yang dilakukan kode Anda tidak mengatakan apakah Anda membaca larik sekali atau berkali-kali.
-
Array mungkin tidak cukup besar ... tergantung pada perangkat keras Anda. (Bukankah beberapa chip modern memiliki cache level 3 berukuran beberapa megabyte?)
-
Khususnya dalam kasus Java, Anda harus melakukan banyak hal dengan cara yang benar untuk menerapkan tolok ukur mikro yang bermakna.
Dalam kasus C:
-
Anda dapat mencoba menyesuaikan sakelar pengoptimalan kompiler C.
-
Karena kode Anda mengakses larik secara berurutan, kompiler mungkin dapat mengurutkan instruksi sehingga CPU dapat mengikuti, atau CPU mungkin secara optimis melakukan prefetching atau melakukan wide fetches. Anda dapat mencoba membaca elemen array dalam urutan yang kurang dapat diprediksi.
-
Bahkan mungkin kompiler telah sepenuhnya mengoptimalkan loop karena hasil perhitungan loop tidak digunakan untuk apa pun.
(Menurut T&J ini - Berapa lama waktu yang diperlukan untuk mengambil satu kata dari memori?, pengambilan dari cache L2 adalah ~7 nanodetik dan pengambilan dari memori utama adalah ~100 nanodetik. Tetapi Anda mendapatkan ~2 nanodetik. Sesuatu yang cerdas harus terjadi di sini untuk membuatnya berjalan secepat yang Anda amati.)