menu

Pelaksanaan dunia yang serba cepat dari Jawa hash peta int-to-int *

* Mikhail Vorontsov dari . Lebih cepat Saya termasuk pengujian aplikasi peta-to-int INTER dalam artikel saya sebelumnya tentang tes dibuat dalam artikel ini

Aku Sebastiano Vigna dan saya ingin berterima kasih Romawi Leventov kebijaksanaan dengan saya mereka peta hybrid berbagi. Beberapa ide “Optimasi Kode: Gunakan Efektif Memory” pelaksanaan terinspirasi oleh Kris Kaspersky  .

Pelaksanaan berbagai trik yang digunakan dalam aplikasi modern dari grafik komposit akan memberikan langkah-demi-langkah gambaran dari artikel ini. Pada akhir artikel ini, mungkin yang tercepat yang tersedia pada saat saya menulis ini hash aplikasi peta artikel-to-int akan menjadi int Java.

Buka pengindeksan

Kebanyakan peta hybrid modern didasarkan pada gagasan yang jelas tentang pengindeksan. Apa artinya? Peta Anda (nilai selalu ditempatkan direktori urut pasangan sekarang akan melupakan mereka) didasarkan pada satu set kunci. Anda harus menemukan kunci untuk set Anda kunci untuk memetakan setiap tindakan. Bagaimana menerapkan?

Pertama-tama, Anda harus mencari jaringan posisi awal. – [1 0 array.length] dapat dihitung dengan integer dalam kisaran setiap pemetaan tombol fungsi. Kunci kode hash adalah bilangan bulat menggunakan metode biasanya penuh. Sebuah fungsi sederhana di sini Math.abs (di key.hashco ()% array.length) mungkin (% Catatan hasilnya bisa negatif). Seperti yang Anda tahu,

Nilai-nilai yang ditetapkan bilangan bulat besar peta dari set kecil kunci (mereka hybrid biasa disebut) berarti Anda berakhir dengan beberapa guncangan - hasil yang sama seperti yang pertama beberapa tombol fungsi. Tabrakan mencoba untuk mengimplementasikan fitur lain dari seri indeks asli telah diselesaikan. Fungsi-fungsi ini sederhana (prevıdx + 1) array.length% . Ada permintaan untuk fungsi-fungsi tersebut - dalam satu lingkaran jika diterapkan, mereka harus menutupi keseluruhan atau garis sel dapat menggunakan kapasitas penuh sehingga direktori. Contoh lain seperti indeks fungsi meningkat perdana panjang urutan nomor adalah bilangan prima.

sel Gratis dan dihapus

Secara teori, itu sudah cukup untuk melaksanakan peta hibrida. Dalam prakteknya, sel membedakan gratis dan sel harus menghapus pendudukan (jika dihapus jika pekerjaan tambahan akan mengangkat sel menghindari menggunakan metode - bagaimana ini) fastutil terakhir dilaksanakan. Dihapus sel "batu nisan" dikenal sebagai.

Kelompok awalnya disebut "sel" penuh kunci bebas. Jika Anda perlu menghapus sebuah saklar yang ada "off" negara, mengatur sel.

Contoh lihatlah:

 Open Directory misalnya

Contoh pengindeksan

peta tombol fungsi pertama int dan masa depan yang disebutkan di atas

1 2 = "DE1"

 Mulai  =   Math .  abs   ( kunci %  seri.  panjang  )  ;  nextıdx  =   (  prevıdx   1  .)  %  seri  panjang  ;  
 Mulai = Math.abs (key array.length%); nextıdx = (prevıdx + 1) array.length%; 

Fakta bahwa kunci yang terkandung dalam peta ini adalah 1, 2, 3 dan 4, tetapi kunci = 3, maka sehingga digantikan dengan satu, telah dihapus dari peta ("RIM") Hapus placeholder.

Kata Kunci Keterangan <:

Mari kita lihat apa yang harus mereka lakukan untuk menemukan tombol berikut / td>

2 mulai pada saat yang sama fungsi indeks = 2 titik dalam sel. Kami memiliki indeks sel = 2 2 = kunci, sehingga ada panggilan lain yang diperlukan.
3 sel ini sehingga fungsi "nextıdx" Anda harus menerapkan "dihapus" dir = 3 Indeks adalah fungsi titik awal sel dalam satu lingkaran kita baik sampai Anda menemukan petunjuk atau sel bebas. Kontrol sel berikutnya = 4 direktori - Sayangnya, kuncinya adalah tidak sama. - Key menemukan bahwa kita tidak bisa menghentikan pencarian, sehingga, ponsel gratis: Kemudian periksa indeks sel = 5.

Berikutnya = kunci kami terbaik = 10 Jika Anda ingin% = kunci array.length add awal Mari kita lihat apa yang akan menjadi 10% 9 = 1 . Indeks = 1 sel, tip lain sudah sibuk, jadi jangan menggunakannya. Jadi indeks sel Indeks Sel = 2 = 3 "dihapus" sehingga kita dapat menempatkan kembali dan 10 kunci.

menumpahkan jika

Hapus sel pembersihan

Petakan Hal ini dapat mengurangi makanan banyak kasus (n 2 ) sel telah dihapus dari peta kompleksitas. Peta diterapkan salah satu cara atau sel pembersihan cepat dihapus. Akibatnya, semua metode peta handset dua kasus akan antara gratis atau digunakan. Selain itu, metode implisit umumnya untuk mendapatkan dan beberapa kompleksitas penghasilan tambahan selama tombol eject mengatur , eksekusi cepat yang kurang metode lain Ini akan dibayar oleh. Artikel ini akan menggunakan logika fastutil membersihkan.

tes kunci

saya sebutkan di atas Indeks Fungsi pertama ( = Math.abs pertama (key array.length%); ) akan menempatkan seri berurutan kunci dari sel berturut-turut. Rantai panjang akan menyebabkan pencarian mengungkapkan peristiwa yang cukup umum, karena ini adalah sel situasi fungsi berikutnya sangat tidak diinginkan jika garis sel berikutnya menggambar saja.

Untuk mencegah hal ini, kita beralih ke "naik" seharusnya, sedikit kami produsen nya. Saya akan mengandalkan kode fastutil mencoba:

 INT_PH dari static final int swasta = 0x9e3779b9; int phimix public static (int x akhir) {h int akhir = x * INT_PH dari; (; Gt; & amp; gt; & amp; amp 16 H) h ^ kembali; Akibatnya} 
 

Panjang rata-rata rantai memegang kunci makan berturut bawah kontrol, akan menjadi nomor urut sel. "Acak" tombol ketika datang ke kasus ini mungkin berakhir dengan distribusi yang cukup bagus pada set kunci, seperti kunci.

Sekarang tentu kami siap mengimplementasikan peta komposit mereka sendiri. Dalam beberapa bagian berikutnya dari artikel ini kita a int-int peta akan dilaksanakan.


Versi 1: dasar int-int peta

(kita akan meninggalkan ruang yang cukup untuk optimasi) Mari kita mulai dengan pelaksanaan sederhana mungkin. Aplikasi ini akan terlihat seperti harta karun 3.0 tıntınthashmap (meskipun saya menyalin kode sumber dengan menulis dalam contoh ini).

menggunakan tiga urutan: satu int [] kunci, dalam int [] dan Boolean [] (true = = digunakan) untuk menggunakan bendera sel. Kami akan mengalokasikan ukuran array yang diperlukan (ini akan menjadi Ukuran / fillfactor + 1 ), praktek-praktek manufaktur kualitas semua bulat meskipun fakta bahwa jumlah.

 1 2 
=" DE1 "

 Mulai  =   Math .  abs   ( Alat.  phimix   (  kunci )  %  seri.   panjang )  ;  nextıdx  =   (  prevıdx   1  )  %  seri  panjang kelas  ;  
 Mulai = Math.abs (tools.phimix (key) array.length%); nextıdx = (prevıdx + 1) array.length%; 

Anda dapat menemukan kode sumber dan semua forum lain di akhir artikel ini.

Saya membandingkan hasil dari penerapan hasil bagian sebelumnya. Untuk membandingkan dengan pelaksanaan lebih cepat Mari - Kolobok (saya hanya akan di mana untuk mendapatkan hasil tes untuk versi peta pencarian untuk menang - Anda dapat menemukan hasil dari artikel di akhir tes penuh). Semua tes untuk artikel ini kelompok acak karyawan kunci. Semua peta mengisi faktor 0,75 memiliki.

Ukuran Peta: 10000 100 000 1000000 10000000 100000000
tests.maptests.primitive.kolobokemutablemaptest 1867 2471 3129 7546 11191
tests.maptests.article_examples.ıntıntmap1test 2768 3671 6105 12313 16 073

ini sudah besar! Kolobok pelaksanaan unoptimized lebih lambat dibandingkan upaya pertama HashMap untuk menerapkan kurang dari 2 kali lipat.

Versi 2: menghindari mahal % operasi - kapasitas sequencing sekarang

Anehnya banyak orang berpikir 2 listrik sangat lambat pada tua kebijaksanaan bilangan bulat divisi / operasi modulo Hal ini tidak berlaku lagi - prosesor baru yang sangat cerdas dan cepat !!! Sayangnya, ini masih benar - dan cukup lambat pembagian integer dan kode kinerja kritis harus dihindari.

HashMap menggunakan versi % metode dijamin untuk dilakukan setidaknya sekali dalam peta sehingga operasi startup berikutnya untuk dua dan perhitungan indeks, Food Search. Ukuran array kami 2 (tinggi pertama 2 kekuatan kapasitas diharapkan) listrik akan sama jika kita dapat menghindarinya.

 1 2 
 Mulai  =  Tools.  phimix   ( kunci )  <. span class = "sy0"> & amp; amp; amp;   ( seri  panjang   -   1  )  ; class  nextıdx  =   ( prevıdx    1  )   & amp; amp; amp;.   ( seri  panjang   -   1  )  ;  
 Mulai = tools.phimix (key) & amp; amp; amp; (Array.length-1); nextıdx = (prevıdx + 1) & amp; amp; (Array.length-1); 

array.length-1 harus, tentu saja, terpisah copy topeng lapangan kelas. Mengapa array.length-1 dapat digunakan sebagai masker? Ini adalah fakta diketahui bahwa jika K = 2 ^ n , di mana adalah XKR% == X amp; (N - 1) . Gunakan dan pengoperasian manfaat tambahan kepada kami tidak selalu negatif (bit tinggi selalu dibersihkan masker tersebut), memberikan perhitungan berakselerasi lain.

Perhatikan bahwa semua peta performa tinggi komposit berdasarkan pada optimasi ini.

Ukuran Peta: 10000

Peta sebelumnya dengan hasil Mari kita bandingkan dengan yang diterapkan td>

100000 1000000 10000000 100000000
tes. maptests.primitive.kolobokemutablemaptest 1867 2471 3129 7546 11191
tests.maptests.article_examples.ıntıntmap1test 2768 3671 6105 12313 16 073
tests.maptests.article_examples.ıntıntmap2test 2254 2767 4869 10543 16 724
optimasi ini tidak hanya memungkinkan kita untuk melakukannya setengah aplikasi dari lambat lebih cepat

! Namun, jalan panjang di depan kami.

Versi 3: untuk menyingkirkan m_used Array

Sebuah peta tiga array untuk menyimpan data peta yang digunakan dalam versi sebelumnya. Kunci acak ini selalu berarti kemungkinan untuk memasukkan peta memori cache CPU menyebabkan wanita tiga wilayah yang berbeda. CPU cache diperlukan untuk meminimalkan jumlah potensi kegiatan kode-kinerja tinggi, merindukan menghasilkan secara normal. Dapat melakukan optimasi sederhana m_used untuk mendapatkan kelompok dan cara cerdas untuk menyandikan penggunaan sel bendera.

Masalahnya adalah kita int-to-int tidak menerapkan peta, sehingga setiap int dapat mengharapkan (beberapa tombol n dicadangkan dan tidak dapat digunakan, menunjukkan ... Tujuan Bagaimana HashMap menyedihkan) digunakan sebagai petunjuk. Ini tidak berarti bahwa kita membutuhkan beberapa penyimpanan ekstra untuk digunakan bendera? Ya, itu adalah. Namun, bukannya titik O (n) O (1) byte penyimpanan adalah bahwa Anda dapat menggunakan!

Kuncinya adalah untuk memilih nilai tertentu dengan ide sel bebas. Setelah dua strategi yang dikenal (saya akan menggunakan yang pertama) adalah:

  1. Toko sesuai dengan nilai di sel bebas di daerah tertentu. Juga rilis kunci sebenarnya digunakan beberapa indikasi dari kebutuhan (a Boolean dan int atau hanya Boolean untuk ketertiban). Setiap peta metode adalah argumen penting, logika yang normal sebelum Periksa bahwa saklar adalah sama dengan bebas dan harus bertindak sesuai.
  2. Kasus tanpa pilih kunci. Jika Anda mencari untuk memperkenalkan pada peta Kasus sebagai peta tidak tersedia dalam kunci rilis baru (lama dan baru gratis dengan nilai mengubah semua kunci). Sekarang Anda perlu menyimpan kunci bebas daripada nilai yang sesuai, tetapi Anda tidak akan mendapatkan manfaat apapun. Kolobok adalah satu-satunya aplikasi yang menggunakan strategi ini. Sebagai catatan

I objek dengan peta gratis saya ingin menyebutkan berurusan dengan tombol tombol yang sangat mudah:

 1 
    private static   akhir   Target  FREE_KEY  =   baru   Target   ( )  ;  
 FREE_KEY Obyek swasta static final = new Object (); Jadi jangan dipraktikkan sebagai peta kunci 

Kunci ini tidak dapat diakses oleh kelas lain. Ingatlah untuk mencerminkan orang-orang pintar peta kunci hash sama dan tidak berlaku untuk bangunan pribadi kode hash metode diingatkan bahwa Anda perlu menerapkan  :)

0 ) digunakan dan> menyimpan nilai dari daerah tertentu:

 1 2 3 4 5 6 7 8 9 10 11 
  swasta   static   akhir   int  FREE_KEY  =   0  ;  & amp; amp; nbsp;  / **  Tombol * /   khusus  int   [ ]  m_keys ;   / ** Nilai * /    int    [ ]  m_values ​​;  & amp; amp; nbsp;  / ** kita berada di peta 'bebas' Apakah ada kunci? * /     khusus  Boolean  m_hasfreekey;  / ** 'bebas' value  tekan kelas * /     int  m_freevalu sebuah ;  
 FREE_KEY int static final swasta = 0; / ** * Tombol / int swasta [] m_keys; / ** * Nilai / int swasta [] m_values; / ** Kami di peta 'bebas' Apakah ada kunci? * / Swasta boolean m_hasfreekey; / ** 'Gratis' * / int swasta m_freevalu nilai kunci; 

Untuk membandingkan hasilnya aplikasi ini dengan peta Biarkan Sebelumnya:

Ukuran Peta 10000 100 000 1000000 10.000. 000 100000000
tests.maptests.primitive.kolobokemutablemaptest 1867 2471 3129 7546 11191
tests.maptests.article_examples.ıntıntmap1test 2768 3671 6105 12313 16 073
tests.maptests .article_examples .ıntıntmap2test 2254 2767 4869 10543 16 724
tests.maptests.article_examples.ıntıntmap3test 2050 2269 3548 9074 13750

Seperti yang Anda lihat, ini mengubah efek dari ukuran peta (Peta besar Anda, Anda kurang berguna untuk mendapatkan peningkatan CPU cache). Namun, kami berada jauh pelaksanaan Kolobok. Namun pada kenyataannya, peta ini Kolobok sekarang dan kemudian fastutil, akan menjadi yang ketiga dalam artikel sebelumnya.

Versi 4 dan 4a - Single

dengan tombol dan nilai-nilai, bukan urutan

Pada langkah ini, ikuti petunjuk pada langkah sebelumnya - sekarang kita ingin menggunakan kunci toko jaringan tunggal dan nilai-nilai. Mereka akan berada di dekat kunci, karena biaya kita banyak nilai-nilai / masukan akan memastikan bahwa perubahan itu mungkin.

int-to-int memiliki 2 kemungkinan aplikasi dalam acara Lokasi: [] selama

  1. Gunakan - kunci dan nilai ketika sel hanya untuk berbagi. Mengenkripsi manfaat dari metode ini terbatas pada jenis dan nilai-nilai tertentu.
  2. Gunakan <> tunggal int [] - akan menjadi kunci dan nilai-nilai yang terkait (hanya strategi bermakna tetap menjadi kunci setelah pencampuran nilai wajar). Petakan strategi ini, kapasitas maksimum 1 milyar sel (Java ukuran kelompok maksimum adalah Integer.MAX_VALUE sama) memiliki sedikit kerugian untuk membatasi. Saya percaya ini menjadi masalah bagi sebagian besar kasus penggunaan.

Perbedaan antara kedua skenario kunci dan Masukkan nilai / adalah kebutuhan untuk menggunakan konversi aritmatika sedikit. Tes saya menunjukkan bahwa operasi ini memiliki dampak negatif yang signifikan pada peta kinerja. Namun, dua [] ini ketika ( ıntıntmap4 ) dan int [] ( ıntıntmap4 ke ) Kami telah mencantumkan versi artikel.

optimasi Micro

Kedua versi akan sangat cepat, tetapi Anda perlu lebih banyak untuk melakukan lebih cepat . Hal yang paling penting yang Anda butuhkan untuk memahami tentang operasi dasar bahwa HashMap O (1) tidak terlalu serakah dengan kompleksitas faktor mengisi disediakan. Mengabaikan fakta bahwa instruksi ini memukul-cara hash (jika sel pertama yang berisi gratis atau diperlukan untuk mengontrol atau switch) berarti. Mengoptimalkan siklus tabrakan hash adalah penting, tetapi (saya ulangi) akan berakhir dengan pandangan campuran operasi Anda, Anda harus sangat berhati-hati karena hit-cara hash.

Dengan pemikiran ini, beberapa metode inline pembantu inline jika Anda ingin buku tertentu, atau Anda dapat menyimpan dua. Lihatlah, misalnya versi sebelumnya Peta untuk mendapatkan metode:

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 
  public   int  untuk  (  akhir   int  kunci )   { & amp; amp; nbsp; & Amp; Amp; nbsp;  jika    ( kunci ==  FREE_KEY  )  & amp; amp; nbsp; & Amp; Amp; nbsp; & Amp; Amp; nbsp; & Amp; Amp; nbsp; ?  Kembali  m_hasfreekey   m_freevalu sebuah   NO_VALU sebuah ;  & amp; amp; nbsp; & Amp; Amp; nbsp; & Amp; Amp; nbsp;   akhir  int  BEI  =  getreadındex  (  kunci )  ;  & amp; amp; nbsp; & Amp; Amp; nbsp; Kembali   BEI  =   -   1  ?  m_values ​​ [ BEI ]    NO_VALU sebuah ;  }  & amp; amp; nbsp;   khusus  int  getreadındex  (  akhir   int  kunci )   { & amp; amp; nbsp; & Amp; Amp; nbsp;  int  BEI  =  getstartındex  ( kunci  )  ;  & amp; amp; nbsp; & Amp; Amp; nbsp;  jika   ( m_keys  [ BEI ]   ==  kunci )   // telepon ini  & amp; amp; nbsp; & Amp; Amp; nbsp; & Amp; Amp; nbsp; & Amp; Amp; nbsp;  Kembali  BEI ;  & amp; amp; nbsp; & Amp; Amp; nbsp;  jika   ( m_keys  [ BEI ]

Leave a Reply

Your email address will not be published. Required fields are marked *