- Mengimplementasikan fungsi untuk menentukan tahun kabisat hanya dengan 3 instruksi CPU
- Metode ini bekerja tanpa percabangan tradisional dengan memanfaatkan operasi bit dan perkalian
- Pendekatan ini akurat untuk rentang tahun 0~102499
- Hasil benchmark menunjukkan performa sekitar 3,8 kali lebih cepat dibanding metode lama
Gambaran umum dan perumusan masalah
- Untuk tahun antara 0 dan 102499, ada fungsi yang menentukan tahun kabisat hanya dengan 3 instruksi CPU
- Fungsi yang digunakan adalah struktur
((y * 1073750999) & 3221352463) <= 126976
- Dijelaskan prinsip, cara kerja, dan kegunaan praktis dari teknik bit-twiddling ini
Metode tradisional untuk menentukan tahun kabisat
- Biasanya penentuan tahun kabisat diimplementasikan dengan pembagian modulo dan percabangan kondisi
Optimasi pendekatan standar
- Pemeriksaan
(y % 100) dapat diganti dengan (y % 25), dan (y % 400) dengan (y % 16)
- Alasannya, karena sebelumnya sudah dibagi 4, hubungan perkalian 25 dan 16 bisa digunakan
- Operasi
(y % 25) dapat diubah menjadi perkalian dan perbandingan tanpa pembagian menggunakan konstanta ajaib
- Contoh: bisa diubah menjadi
x * 3264175145 > 171798691
- Dengan menambahkan bitmask,
(y & 3) atau (y & 15) dapat menggantikan pembagian oleh 4 atau 16
- Compiler dapat mengotomatiskan transformasi ini, tetapi pendekatan tersebut juga bisa dimanfaatkan langsung di bahasa lain
Implementasi tanpa percabangan (branchless)
Mencari konstanta ajaib: pendekatan bit-twiddling
- Untuk membuat ekspresi penentuan tahun kabisat lebih sederhana, digunakan pencarian kombinatorial dan Z3, sebuah SMT Solver
- Bentuknya:
((y * f) & m) <= t
- Konstanta f, m, t yang memenuhi syarat dicari dengan Z3
- Ditemukan nilai yang menghasilkan hasil akurat pada rentang 0~102499
- Hasil akhirnya adalah
(y * 1073750999) & 3221352463 <= 126976
Penjelasan prinsip fungsi dan struktur internal
- Konstanta dianalisis dalam bentuk biner lalu dibagi menjadi tiga area bit utama: A, B, C
- Bergantung pada status bit di tiap area, tiga syarat penentuan tahun kabisat dapat dicakup
- Pemecahan logika fungsi:
- Area A: memeriksa kondisi kelipatan 4, termasuk apakah
(y % 4) != 0
- Area B: memfilter apakah
(y % 100) != 0 dengan berbagai pola, misalnya akhiran 14, 57, 71, dan lain-lain
- Area C: memeriksa apakah
(y % 16) == 0, yaitu apakah merupakan kelipatan 16
- Dijelaskan prinsip bahwa perkalian secara efektif menggabungkan berbagai kondisi tanpa benar-benar menghitung sisa bagi
- Saat mengalikan dengan konstanta ajaib, pola bit khusus terbentuk pada kelipatan 100 dan kasus lain
- Selain itu, juga dibahas struktur matematis internal seperti galat perkalian dan pola angka multigit yang muncul
Kemungkinan perluasan rentang dan lebar bit
- Jika diperluas ke 64-bit, juga dijelaskan cara mencari kombinasi konstanta ajaib yang sesuai
- Dengan mencoba berbagai nilai
f, m, t, dapat dicari rentang terpanjang
- Di StackExchange juga ada contoh kombinasi optimal dan pembuktian dengan Z3
Benchmark dan perbandingan performa nyata
- Hasil benchmark:
- Untuk nilai yang dapat diprediksi seperti 2025, hampir tidak ada perbedaan pada 0.65~0.69ns
- Untuk input acak,
is_leap_year_fast menunjukkan performa sekitar 3,8 kali lebih cepat
- Jika pola input membuat percabangan sulit diprediksi, pendekatan branching ini memiliki keuntungan besar
Kesimpulan dan apakah layak diterapkan
- Dalam aplikasi nyata, keuntungannya kecil jika nilainya mudah diprediksi, tetapi sangat berguna dalam situasi dengan data acak dalam jumlah besar
- Jika ingin mengganti pustaka standar nyata seperti di Python atau C#, perlu dilakukan benchmark program secara keseluruhan yang realistis
- Ide dan metode implementasinya sendiri menarik, dan dalam situasi tertentu merupakan solusi yang atraktif dari sisi performa
2 komentar
Tulisan yang mengingatkan pada fast inverse sqrt
Opini Hacker News
is_leap_year1menjadi sepertiis_leap_year2, jadi rasanya tidak terlalu perlu melakukan optimisasi langsung di level source C, meskipun ini mungkin masih berguna di bahasa lain, terutama karena menarik melihat versi terbaru source codecalmenangani pemeriksaan tahun kabisat dengan sangat sederhana((y * 1073750999) & 3221352463) <= 126976pasti rumit; justru itu memang wajarCMP(X, Y)yang efisien untuk perbandingan gaya UNIX, contoh optimisasi fungsi signum, contoh kode assembly untuk Motorola 68000, kumpulan makro bit yang berasal dari OpenSolaris, dan berbagai teknik optimisasi lain, sambil menyesalkan blog Open Solaris sudah hilang; direkomendasikan untuk orang yang tertarik pada optimisasi kode