java performance modulo

¿Es el módulo lento en Java?



performance modulo (2)

Se evita el % por razones de rendimiento en este ejemplo.

div operaciones div / rem son más lentas incluso en el nivel de arquitectura de la CPU; No solo en Java. Por ejemplo, la latencia mínima de la instrucción idiv en Haswell es de aproximadamente 10 ciclos, pero solo 1 ciclo para add .

Vamos a comparar utilizando JMH .

import org.openjdk.jmh.annotations.*; @State(Scope.Benchmark) public class Modulo { @Param("16") int len; int i; @Benchmark public int baseline() { return i; } @Benchmark public int conditional() { return i = (i + 1 < len) ? i + 1 : 0; } @Benchmark public int mask() { return i = (i + 1) & (len - 1); } @Benchmark public int mod() { return i = (i + 1) % len; } }

Resultados:

Benchmark (len) Mode Cnt Score Error Units Modulo.baseline 16 avgt 10 2,951 ± 0,038 ns/op Modulo.conditional 16 avgt 10 3,517 ± 0,051 ns/op Modulo.mask 16 avgt 10 3,765 ± 0,016 ns/op Modulo.mod 16 avgt 10 9,125 ± 0,023 ns/op

Como puede ver, usar % es ~ 2.6x más lento que una expresión condicional. JIT no puede optimizar esto automáticamente en el código ThreadLocal discutido, porque el divisor ( table.length ) es variable.

He estado mirando la implementación de ThreadLocal en el JDK, por curiosidad, y encontré esto:

/** * Increment i modulo len. */ private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); }

Parece bastante obvio que esto podría implementarse con un simple return (i + 1) % len , pero creo que estos chicos saben lo que hacen. ¿Alguna idea de por qué hicieron esto?

Este código está altamente orientado hacia el rendimiento, con un mapa personalizado para contener asignaciones de subprocesos locales, referencias débiles para ayudar al GC a ser inteligente, etc., así que supongo que esto es una cuestión de rendimiento. ¿Es el módulo lento en Java?


mod no es tan lento en Java. Se implementa como las instrucciones de código de byte en frem de enteros y frem respectivamente. El JIT hace un buen trabajo optimizando esto.

En mis puntos de referencia (ver article ), irem llamadas en JDK 1.8 toman alrededor de 1 nanosegundo . Eso es bastante rápido. frem llamadas frem son aproximadamente 3x más lentas, así que use números enteros cuando sea posible.

Si está utilizando enteros naturales (p. Ej., Indexación de matrices) y una potencia de 2 divisores (p. Ej., 8 subprocesos locales), entonces puede usar un truco de manipulación de bits para obtener un 20% de ganancia de rendimiento.