- Implementa una función que determina si un año es bisiesto usando solo 3 instrucciones de CPU
- Este método funciona sin ramas tradicionales, utilizando operaciones de bits y multiplicación
- Este enfoque es exacto en el rango de 0 a 102499 años
- Los resultados del benchmark muestran un rendimiento aproximadamente 3.8 veces más rápido que el método existente
Descripción general y planteamiento del problema
- Para años entre 0 y 102499, presenta una función que determina si un año es bisiesto usando solo 3 instrucciones de CPU
- La función real utilizada tiene la forma
((y * 1073750999) & 3221352463) <= 126976
- Explica el principio, el funcionamiento y la utilidad práctica de esta técnica de bit-twiddling
Método tradicional para determinar años bisiestos
- Normalmente, la comprobación de año bisiesto se implementa con divisiones (módulo) y ramas condicionales
Optimización del enfoque estándar
- La comprobación de
(y % 100) puede reemplazarse por (y % 25), y la de (y % 400) por (y % 16)
- La razón es que antes ya se dividió entre 4, por lo que puede transformarse usando la relación de multiplicación por 25 y 16
- Una constante mágica para convertir la operación
(y % 25) en multiplicación y comparación sin división
- Por ejemplo, puede transformarse en
x * 3264175145 > 171798691
- También se puede sumar una máscara de bits para sustituir divisiones entre 4 o 16 con
(y & 3) o (y & 15)
- Los compiladores automatizan estas transformaciones, pero también pueden aprovecharse manualmente en otros lenguajes
Implementación sin ramas (branchless)
Búsqueda de constantes mágicas: enfoque de bit-twiddling
- Para hacer la expresión de año bisiesto aún más simple, se utilizó búsqueda combinatoria y el SMT Solver Z3
- Forma:
((y * f) & m) <= t
- Se buscaron con Z3 las constantes
f, m y t que cumplen las condiciones requeridas
- Se encontraron valores que dan resultados correctos en el rango de 0 a 102499
- El resultado final es
(y * 1073750999) & 3221352463 <= 126976
Explicación del principio de la función y su estructura interna
- Al analizar las constantes en binario, se dividen en tres regiones principales de bits: A, B y C
- Según el estado de los bits en cada región, se abarcan las 3 condiciones de la comprobación de año bisiesto
- Descomposición lógica de la función:
- Región A: verifica la condición de múltiplo de 4, incluida la comprobación de si
(y % 4) != 0
- Región B: filtra si
(y % 100) != 0 mediante varios patrones (por ejemplo, terminaciones 14, 57, 71, etc.)
- Región C: comprueba si
(y % 16) == 0, es decir, si es múltiplo de 16
- Explica cómo la multiplicación realmente combina varias condiciones sin calcular restos de forma explícita
- Al multiplicar por la constante mágica, se forman patrones de bits característicos en múltiplos de 100 y otros casos
- Además, incluye un análisis de la estructura matemática interna donde aparecen errores de multiplicación y patrones numéricos de varias cifras
Posible ampliación de rango y ancho de bits
- También se presenta un método para buscar combinaciones adecuadas de constantes mágicas al extenderlo a 64 bits
- Probando distintos valores de
f, m y t, se puede encontrar el rango más amplio posible
- En StackExchange también hay casos de prueba del uso de Z3 y de combinaciones óptimas
Benchmark y comparación de rendimiento real
- Resultados del benchmark:
- En valores predecibles como 2025, casi no hay diferencia, con 0.65~0.69ns
- Con entradas aleatorias,
is_leap_year_fast muestra un rendimiento aproximadamente 3.8 veces superior
- Según el patrón de entrada, el enfoque con ramas puede volverse impredecible y aquí existe una ventaja considerable
Conclusión y aplicabilidad real
- En aplicaciones reales, si los valores son predecibles, la ganancia es pequeña, pero es muy útil en escenarios con grandes volúmenes de datos aleatorios
- Si se quiere reemplazar una biblioteca estándar real en Python o C#, hace falta un benchmark realista del programa completo
- La idea y el método de implementación en sí son interesantes, y en ciertos casos son una solución atractiva en términos de rendimiento
2 comentarios
Un texto que recuerda al fast inverse sqrt
Opiniones en Hacker News
is_leap_year1para convertirlo en algo comois_leap_year2, así que no parece muy necesario optimizarlo a mano en la etapa del código fuente en C, aunque todavía puede ser útil en otros lenguajes; en particular, impresiona lo simple que resuelve la verificación de año bisiesto el código fuente de la versión reciente del programacal((y * 1073750999) & 3221352463) <= 126976, termine siendo complicada; más bien, es lo naturalCMP(X, Y)eficiente para comparaciones al estilo UNIX, ejemplos de optimización de la función signum, ejemplos de código ensamblador para Motorola 68000 y colecciones de macros de bits originadas en OpenSolaris, entre otras técnicas de optimización; también mencionan con pena que el blog de Open Solaris ya no existe, y lo recomiendan a quienes se interesan por optimización de código