- Une fonction est implémentée pour déterminer si une année est bissextile avec seulement 3 instructions CPU
- Cette méthode fonctionne sans branchement traditionnel, en utilisant des opérations sur les bits et une multiplication
- Cette approche est exacte pour la plage de 0 à 102499
- Les benchmarks montrent des performances environ 3,8 fois plus rapides que la méthode classique
Vue d’ensemble et formulation du problème
- Pour les années comprises entre 0 et 102499, une fonction qui détermine si une année est bissextile en n’utilisant que 3 instructions CPU
- La fonction effectivement utilisée est de la forme
((y * 1073750999) & 3221352463) <= 126976
- Explication du principe, du fonctionnement et de l’utilité concrète de cette technique de bit-twiddling
Méthode traditionnelle de détection des années bissextiles
- En général, la détection d’une année bissextile est implémentée avec des divisions modulo et des branchements conditionnels
Optimisation de l’approche standard
- Il est possible de remplacer le test
(y % 100) par (y % 25) et le test (y % 400) par (y % 16)
- La raison est qu’on a déjà divisé par 4 au préalable, ce qui permet de reformuler les relations via 25 et 16
- Une constante magique permet de transformer l’opération
(y % 25) en multiplication et comparaison sans division
- Par exemple : conversion possible en
x * 3264175145 > 171798691
- En ajoutant un masque de bits, on peut remplacer des divisions par 4 ou 16 avec
(y & 3) ou (y & 15)
- Les compilateurs automatisent ces transformations, mais elles peuvent aussi être exploitées manuellement dans d’autres langages
Implémentation sans branchement
Recherche des constantes magiques : approche bit-twiddling
- Pour rendre la formule de détection d’une année bissextile encore plus simple, une recherche combinatoire et le solveur SMT Z3 sont utilisés
- Forme :
((y * f) & m) <= t
- Les constantes f, m, t satisfaisant les contraintes sont recherchées avec Z3
- Cela permet de trouver des valeurs donnant un résultat exact sur l’intervalle 0~102499
- Le résultat final est
(y * 1073750999) & 3221352463 <= 126976
Explication du principe de la fonction et de sa structure interne
- En analysant les constantes en binaire, elles sont divisées en trois grandes zones de bits : A, B, C
- Selon l’état des bits dans chaque zone, elles couvrent les 3 conditions de la détection d’une année bissextile
- Décomposition logique de la fonction :
- Zone A : vérifie la condition de multiple de 4, notamment
(y % 4) != 0
- Zone B : filtre la condition
(y % 100) != 0 à l’aide de divers motifs, par exemple des fins de nombre comme 14, 57, 71, etc.
- Zone C : vérifie
(y % 16) == 0, c’est-à-dire le fait d’être un multiple de 16
- Explication du mécanisme par lequel la multiplication combine plusieurs conditions sans calculer explicitement de reste
- Lorsqu’on multiplie par la constante magique, des motifs de bits particuliers apparaissent pour les multiples de 100, etc.
- L’analyse inclut aussi la structure mathématique interne liée aux erreurs de multiplication et à l’apparition de motifs sur plusieurs chiffres
Extension possible de la plage et de la largeur en bits
- En l’étendant à 64 bits, l’article présente aussi une méthode pour rechercher des combinaisons adaptées de constantes magiques
- On peut faire varier les valeurs de
f, m, t pour trouver la plage maximale
- On trouve également sur StackExchange des exemples de combinaison optimale et de preuve utilisant Z3
Benchmarks et comparaison des performances réelles
- Résultats des benchmarks :
- Pour des valeurs prévisibles comme 2025, la différence est presque nulle, avec 0,65 à 0,69 ns
- Avec des entrées aléatoires,
is_leap_year_fast affiche des performances environ 3,8 fois supérieures
- Lorsque le schéma d’entrée rend les branchements imprévisibles, cette approche apporte un avantage significatif
Conclusion et pertinence en pratique
- Dans les cas réels où les valeurs sont prévisibles, le gain est faible, mais la méthode est très utile sur de grands volumes de données aléatoires
- Pour remplacer une bibliothèque standard en Python, C# ou ailleurs, un benchmark réaliste sur l’ensemble du programme reste nécessaire
- L’idée et la méthode d’implémentation sont intéressantes en elles-mêmes, et constituent dans certains contextes une solution attrayante du point de vue des performances
2 commentaires
Un texte qui fait penser au fast inverse sqrt
Avis Hacker News
is_leap_year1en quelque chose commeis_leap_year2, ce qui donne à penser qu’il n’est généralement pas nécessaire d’optimiser soi-même au niveau du code source C ; cela peut toutefois rester utile dans d’autres langages, et il est particulièrement impressionnant de voir à quel point la vérification des années bissextiles est traitée simplement dans la version récente du code source decal((y * 1073750999) & 3221352463) <= 126976soit forcément compliquée ; c’est même parfaitement normalCMP(X, Y)efficace pour les comparaisons à la UNIX, exemple d’optimisation de la fonction signum, exemple de code assembleur pour Motorola 68000, ensemble de macros bit à bit issues d’OpenSolaris, etc. ; avec aussi le regret que le blog Open Solaris ait disparu ; recommandé à tous ceux qui s’intéressent à l’optimisation du code