234 votos

¿Por qué Java interruptor en contiguos enteros parecen correr más rápido con el agregado de los casos?

Estoy trabajando en algunos de Java de código que debe ser altamente optimizado como se ejecutará en caliente funciones que se invocan en muchos puntos de mi programa principal de la lógica. Parte de este código consiste en multiplicar double variables 10 planteado para arbitrario no negativo int exponents. Una manera rápida (edit: pero no de la forma más rápida posible, consulte Actualización de 2 a continuación) para obtener el valor multiplicado es switch sobre el exponent:

double multiplyByPowerOfTen(final double d, final int exponent) {
   switch (exponent) {
      case 0:
         return d;
      case 1:
         return d*10;
      case 2:
         return d*100;
      // ... same pattern
      case 9:
         return d*1000000000;
      case 10:
         return d*10000000000L;
      // ... same pattern with long literals
      case 18:
         return d*1000000000000000000L;
      default:
         throw new ParseException("Unhandled power of ten " + power, 0);
   }
}

El comentó puntos suspensivos indican que el case int constantes de continuar incrementando por 1, por lo que hay realmente un 19 cases en el anterior fragmento de código. Como yo no estaba seguro de si sería realmente necesario que todas las potencias de 10 en case declaraciones 10 thru 18, me encontré con algunos microbenchmarks comparación entre el tiempo para completar los 10 millones de operaciones con este switch declaración frente a un switch con sólo cases 0 thru 9 (con el exponent limitado a 9 o menos para evitar que se rompa el depurada switch). Tengo el más sorprendente (para mí, al menos!) resultado que el más switch con más case declaraciones realmente corría más rápido.

En una alondra, he intentado añadir aún más cases, que acaba de regresar valores ficticios, y se encontró que podía conseguir el interruptor para correr aún más rápido con alrededor de 22 a 27 declaró cases (aunque los ficticio de los casos nunca son en realidad un golpe mientras se ejecuta el código). (De nuevo, cases se añadieron en un contiguos de la moda por el incremento de la anterior case constante 1.) Estos tiempo de ejecución de las diferencias no son muy significativos: por un aleatorios exponent entre 0 y 10, el chupete collar switch instrucción finaliza a los 10 millones de ejecuciones en 1.49 segundos frente a 1.54 segundos para el no acolchadas versión, para un gran total de ahorro de 5ns por ejecución. Así, no es el tipo de cosa que hace que obsesionarse con el relleno de un switch declaración de la pena el esfuerzo de una optimización del punto de vista. Pero todavía me resulta curioso y contra-intuitiva de que un switch no se vuelve más lento (o tal vez en el mejor de mantener constante O(1) tiempo) para ejecutar como más cases se agregan a él.

switch benchmarking results

Estos son los resultados que he obtenido de la ejecución, con diferentes límites en la generada aleatoriamente exponent valores. Yo no incluyen los resultados de todo el camino hacia abajo para 1 de la exponent límite, pero la forma general de la curva sigue siendo el mismo, con reborde en torno a la 12-17 caso de que marca, y el valle entre 18-28. Todas las pruebas se ejecutan en JUnitBenchmarks el uso compartido de contenedores para los valores aleatorios para asegurar pruebas idénticas entradas. También me encontré con las pruebas, tanto en el orden de más largo switch declaración a menor, y viceversa, para tratar y eliminar la posibilidad de pedir relacionados con problemas de las pruebas. He puesto mi código de prueba en un repo en github si alguien quiere tratar de reproducir estos resultados.

Así que, ¿qué está pasando aquí? Algunos de los caprichos de mi arquitectura o micro-punto de referencia de la construcción? O es el Java switch realmente un poco más rápido para ejecutar en el 18 a 28 case rango de lo que es de 11 a 17?

github prueba repo "switch-experimento"

ACTUALIZACIÓN: he limpiado el benchmarking de la biblioteca un poco y se añade un archivo de texto en /resultados con algunas de salida a través de una amplia gama de posibles exponent valores. También he añadido una opción en el código de prueba no tirar un Exception de default, pero esto no parece afectar a los resultados.

ACTUALIZACIÓN 2: Encontrado algunos muy buenos discusión de esta cuestión en el 2009 en el xkcd foro aquí: http://forums.xkcd.com/viewtopic.php?f=11&t=33524. El OP de la discusión del uso de Array.binarySearch() me dio la idea de una matriz simple basado en la aplicación de la exponenciación patrón de arriba. No hay necesidad de que el binario de búsqueda, ya sé lo de las entradas en la array son. Parece que se ejecutan alrededor de 3 veces más rápido que usando switch, obviamente a costa de algunos de que el flujo de control que switch ofrece. Que el código se ha agregado la repo de github también.

198voto

assylias Puntos 102015

Como se ha señalado por la otra respuesta, porque en el caso de que los valores son contiguos (como contraposición a la escasa), el bytecode generado para las diferentes pruebas se utiliza un interruptor (tabla de código de bytes de la instrucción tableswitch).

Sin embargo, una vez que el JIT comienza su trabajo y compila el código de bytes en la asamblea, la tableswitch de la instrucción no siempre el resultado en un array de punteros: a veces el interruptor de la tabla se transforma en lo que parece un lookupswitch (similar a un if/else if estructura).

O descompilación, a la asamblea generado por el compilador JIT (hotspot JDK 1.7) muestra que usa una sucesión de si/si por el contrario, cuando hay 17 casos o menos, una matriz de punteros cuando hay más de 18 años (la más eficiente).

La razón por la que este número mágico de 18 años es utilizado parece venir hacia el valor predeterminado de la MinJumpTableSize JVM bandera (alrededor de la línea 352 en el código).

He planteado el problema en el hotspot del compilador de la lista y parece ser un legado de pruebas anteriores. Tenga en cuenta que este valor predeterminado se ha eliminado en el JDK 8 después de más de benchmarking se realizó.

Finalmente, cuando el método se vuelve demasiado largo (> 25 casos, en mis pruebas), no es en línea por más tiempo con el valor predeterminado de la configuración de la JVM - que es la más probable causa de la caída en el rendimiento en ese momento.


Con 5 casos, el descompilados de código tiene este aspecto (aviso cmp/je/jg/jmp instrucciones, la asamblea si/goto):

[Verified Entry Point]
  # {method} 'multiplyByPowerOfTen' '(DI)D' in 'javaapplication4/Test1'
  # parm0:    xmm0:xmm0   = double
  # parm1:    rdx       = int
  #           [sp+0x20]  (sp of caller)
  0x00000000024f0160: mov    DWORD PTR [rsp-0x6000],eax
                                                ;   {no_reloc}
  0x00000000024f0167: push   rbp
  0x00000000024f0168: sub    rsp,0x10           ;*synchronization entry
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@-1 (line 56)
  0x00000000024f016c: cmp    edx,0x3
  0x00000000024f016f: je     0x00000000024f01c3
  0x00000000024f0171: cmp    edx,0x3
  0x00000000024f0174: jg     0x00000000024f01a5
  0x00000000024f0176: cmp    edx,0x1
  0x00000000024f0179: je     0x00000000024f019b
  0x00000000024f017b: cmp    edx,0x1
  0x00000000024f017e: jg     0x00000000024f0191
  0x00000000024f0180: test   edx,edx
  0x00000000024f0182: je     0x00000000024f01cb
  0x00000000024f0184: mov    ebp,edx
  0x00000000024f0186: mov    edx,0x17
  0x00000000024f018b: call   0x00000000024c90a0  ; OopMap{off=48}
                                                ;*new  ; - javaapplication4.Test1::multiplyByPowerOfTen@72 (line 83)
                                                ;   {runtime_call}
  0x00000000024f0190: int3                      ;*new  ; - javaapplication4.Test1::multiplyByPowerOfTen@72 (line 83)
  0x00000000024f0191: mulsd  xmm0,QWORD PTR [rip+0xffffffffffffffa7]        # 0x00000000024f0140
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@52 (line 62)
                                                ;   {section_word}
  0x00000000024f0199: jmp    0x00000000024f01cb
  0x00000000024f019b: mulsd  xmm0,QWORD PTR [rip+0xffffffffffffff8d]        # 0x00000000024f0130
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@46 (line 60)
                                                ;   {section_word}
  0x00000000024f01a3: jmp    0x00000000024f01cb
  0x00000000024f01a5: cmp    edx,0x5
  0x00000000024f01a8: je     0x00000000024f01b9
  0x00000000024f01aa: cmp    edx,0x5
  0x00000000024f01ad: jg     0x00000000024f0184  ;*tableswitch
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
  0x00000000024f01af: mulsd  xmm0,QWORD PTR [rip+0xffffffffffffff81]        # 0x00000000024f0138
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@64 (line 66)
                                                ;   {section_word}
  0x00000000024f01b7: jmp    0x00000000024f01cb
  0x00000000024f01b9: mulsd  xmm0,QWORD PTR [rip+0xffffffffffffff67]        # 0x00000000024f0128
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@70 (line 68)
                                                ;   {section_word}
  0x00000000024f01c1: jmp    0x00000000024f01cb
  0x00000000024f01c3: mulsd  xmm0,QWORD PTR [rip+0xffffffffffffff55]        # 0x00000000024f0120
                                                ;*tableswitch
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
                                                ;   {section_word}
  0x00000000024f01cb: add    rsp,0x10
  0x00000000024f01cf: pop    rbp
  0x00000000024f01d0: test   DWORD PTR [rip+0xfffffffffdf3fe2a],eax        # 0x0000000000430000
                                                ;   {poll_return}
  0x00000000024f01d6: ret    

Con 18 casos, la asamblea se parece a esto (aviso de la matriz de indicadores que se utiliza y se suprime la necesidad de que todas las comparaciones: jmp QWORD PTR [r8+r10*1] salta directamente a la derecha de la multiplicación) - que es la razón probable para la mejora del rendimiento:

[Verified Entry Point]
  # {method} 'multiplyByPowerOfTen' '(DI)D' in 'javaapplication4/Test1'
  # parm0:    xmm0:xmm0   = double
  # parm1:    rdx       = int
  #           [sp+0x20]  (sp of caller)
  0x000000000287fe20: mov    DWORD PTR [rsp-0x6000],eax
                                                ;   {no_reloc}
  0x000000000287fe27: push   rbp
  0x000000000287fe28: sub    rsp,0x10           ;*synchronization entry
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@-1 (line 56)
  0x000000000287fe2c: cmp    edx,0x13
  0x000000000287fe2f: jae    0x000000000287fe46
  0x000000000287fe31: movsxd r10,edx
  0x000000000287fe34: shl    r10,0x3
  0x000000000287fe38: movabs r8,0x287fd70       ;   {section_word}
  0x000000000287fe42: jmp    QWORD PTR [r8+r10*1]  ;*tableswitch
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
  0x000000000287fe46: mov    ebp,edx
  0x000000000287fe48: mov    edx,0x31
  0x000000000287fe4d: xchg   ax,ax
  0x000000000287fe4f: call   0x00000000028590a0  ; OopMap{off=52}
                                                ;*new  ; - javaapplication4.Test1::multiplyByPowerOfTen@202 (line 96)
                                                ;   {runtime_call}
  0x000000000287fe54: int3                      ;*new  ; - javaapplication4.Test1::multiplyByPowerOfTen@202 (line 96)
  0x000000000287fe55: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe8b]        # 0x000000000287fce8
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@194 (line 92)
                                                ;   {section_word}
  0x000000000287fe5d: jmp    0x000000000287ff16
  0x000000000287fe62: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe86]        # 0x000000000287fcf0
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@188 (line 90)
                                                ;   {section_word}
  0x000000000287fe6a: jmp    0x000000000287ff16
  0x000000000287fe6f: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe81]        # 0x000000000287fcf8
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@182 (line 88)
                                                ;   {section_word}
  0x000000000287fe77: jmp    0x000000000287ff16
  0x000000000287fe7c: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe7c]        # 0x000000000287fd00
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@176 (line 86)
                                                ;   {section_word}
  0x000000000287fe84: jmp    0x000000000287ff16
  0x000000000287fe89: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe77]        # 0x000000000287fd08
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@170 (line 84)
                                                ;   {section_word}
  0x000000000287fe91: jmp    0x000000000287ff16
  0x000000000287fe96: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe72]        # 0x000000000287fd10
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@164 (line 82)
                                                ;   {section_word}
  0x000000000287fe9e: jmp    0x000000000287ff16
  0x000000000287fea0: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe70]        # 0x000000000287fd18
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@158 (line 80)
                                                ;   {section_word}
  0x000000000287fea8: jmp    0x000000000287ff16
  0x000000000287feaa: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe6e]        # 0x000000000287fd20
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@152 (line 78)
                                                ;   {section_word}
  0x000000000287feb2: jmp    0x000000000287ff16
  0x000000000287feb4: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe24]        # 0x000000000287fce0
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@146 (line 76)
                                                ;   {section_word}
  0x000000000287febc: jmp    0x000000000287ff16
  0x000000000287febe: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe6a]        # 0x000000000287fd30
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@140 (line 74)
                                                ;   {section_word}
  0x000000000287fec6: jmp    0x000000000287ff16
  0x000000000287fec8: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe68]        # 0x000000000287fd38
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@134 (line 72)
                                                ;   {section_word}
  0x000000000287fed0: jmp    0x000000000287ff16
  0x000000000287fed2: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe66]        # 0x000000000287fd40
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@128 (line 70)
                                                ;   {section_word}
  0x000000000287feda: jmp    0x000000000287ff16
  0x000000000287fedc: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe64]        # 0x000000000287fd48
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@122 (line 68)
                                                ;   {section_word}
  0x000000000287fee4: jmp    0x000000000287ff16
  0x000000000287fee6: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe62]        # 0x000000000287fd50
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@116 (line 66)
                                                ;   {section_word}
  0x000000000287feee: jmp    0x000000000287ff16
  0x000000000287fef0: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe60]        # 0x000000000287fd58
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@110 (line 64)
                                                ;   {section_word}
  0x000000000287fef8: jmp    0x000000000287ff16
  0x000000000287fefa: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe5e]        # 0x000000000287fd60
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@104 (line 62)
                                                ;   {section_word}
  0x000000000287ff02: jmp    0x000000000287ff16
  0x000000000287ff04: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe5c]        # 0x000000000287fd68
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@98 (line 60)
                                                ;   {section_word}
  0x000000000287ff0c: jmp    0x000000000287ff16
  0x000000000287ff0e: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe12]        # 0x000000000287fd28
                                                ;*tableswitch
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
                                                ;   {section_word}
  0x000000000287ff16: add    rsp,0x10
  0x000000000287ff1a: pop    rbp
  0x000000000287ff1b: test   DWORD PTR [rip+0xfffffffffd9b00df],eax        # 0x0000000000230000
                                                ;   {poll_return}
  0x000000000287ff21: ret    

Y, finalmente, la asamblea, con 30 casos (abajo) es similar a 18 casos, excepto para el adicional movapd xmm0,xmm1 que aparece hacia la mitad del código, como manchado por @cHao - sin embargo, lo más probable razón de la disminución en el rendimiento es que el método es demasiado tiempo para estar en línea con el valor predeterminado de la configuración de la JVM:

[Verified Entry Point]
  # {method} 'multiplyByPowerOfTen' '(DI)D' in 'javaapplication4/Test1'
  # parm0:    xmm0:xmm0   = double
  # parm1:    rdx       = int
  #           [sp+0x20]  (sp of caller)
  0x0000000002524560: mov    DWORD PTR [rsp-0x6000],eax
                                                ;   {no_reloc}
  0x0000000002524567: push   rbp
  0x0000000002524568: sub    rsp,0x10           ;*synchronization entry
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@-1 (line 56)
  0x000000000252456c: movapd xmm1,xmm0
  0x0000000002524570: cmp    edx,0x1f
  0x0000000002524573: jae    0x0000000002524592  ;*tableswitch
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
  0x0000000002524575: movsxd r10,edx
  0x0000000002524578: shl    r10,0x3
  0x000000000252457c: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe3c]        # 0x00000000025243c0
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@364 (line 118)
                                                ;   {section_word}
  0x0000000002524584: movabs r8,0x2524450       ;   {section_word}
  0x000000000252458e: jmp    QWORD PTR [r8+r10*1]  ;*tableswitch
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
  0x0000000002524592: mov    ebp,edx
  0x0000000002524594: mov    edx,0x31
  0x0000000002524599: xchg   ax,ax
  0x000000000252459b: call   0x00000000024f90a0  ; OopMap{off=64}
                                                ;*new  ; - javaapplication4.Test1::multiplyByPowerOfTen@370 (line 120)
                                                ;   {runtime_call}
  0x00000000025245a0: int3                      ;*new  ; - javaapplication4.Test1::multiplyByPowerOfTen@370 (line 120)
  0x00000000025245a1: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe27]        # 0x00000000025243d0
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@358 (line 116)
                                                ;   {section_word}
  0x00000000025245a9: jmp    0x0000000002524744
  0x00000000025245ae: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe22]        # 0x00000000025243d8
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@348 (line 114)
                                                ;   {section_word}
  0x00000000025245b6: jmp    0x0000000002524744
  0x00000000025245bb: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe1d]        # 0x00000000025243e0
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@338 (line 112)
                                                ;   {section_word}
  0x00000000025245c3: jmp    0x0000000002524744
  0x00000000025245c8: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe18]        # 0x00000000025243e8
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@328 (line 110)
                                                ;   {section_word}
  0x00000000025245d0: jmp    0x0000000002524744
  0x00000000025245d5: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe13]        # 0x00000000025243f0
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@318 (line 108)
                                                ;   {section_word}
  0x00000000025245dd: jmp    0x0000000002524744
  0x00000000025245e2: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe0e]        # 0x00000000025243f8
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@308 (line 106)
                                                ;   {section_word}
  0x00000000025245ea: jmp    0x0000000002524744
  0x00000000025245ef: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe09]        # 0x0000000002524400
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@298 (line 104)
                                                ;   {section_word}
  0x00000000025245f7: jmp    0x0000000002524744
  0x00000000025245fc: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe04]        # 0x0000000002524408
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@288 (line 102)
                                                ;   {section_word}
  0x0000000002524604: jmp    0x0000000002524744
  0x0000000002524609: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffdff]        # 0x0000000002524410
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@278 (line 100)
                                                ;   {section_word}
  0x0000000002524611: jmp    0x0000000002524744
  0x0000000002524616: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffdfa]        # 0x0000000002524418
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@268 (line 98)
                                                ;   {section_word}
  0x000000000252461e: jmp    0x0000000002524744
  0x0000000002524623: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffd9d]        # 0x00000000025243c8
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@258 (line 96)
                                                ;   {section_word}
  0x000000000252462b: jmp    0x0000000002524744
  0x0000000002524630: movapd xmm0,xmm1
  0x0000000002524634: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe0c]        # 0x0000000002524448
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@242 (line 92)
                                                ;   {section_word}
  0x000000000252463c: jmp    0x0000000002524744
  0x0000000002524641: movapd xmm0,xmm1
  0x0000000002524645: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffddb]        # 0x0000000002524428
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@236 (line 90)
                                                ;   {section_word}
  0x000000000252464d: jmp    0x0000000002524744
  0x0000000002524652: movapd xmm0,xmm1
  0x0000000002524656: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffdd2]        # 0x0000000002524430
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@230 (line 88)
                                                ;   {section_word}
  0x000000000252465e: jmp    0x0000000002524744
  0x0000000002524663: movapd xmm0,xmm1
  0x0000000002524667: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffdc9]        # 0x0000000002524438
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@224 (line 86)
                                                ;   {section_word}

[etc.]

  0x0000000002524744: add    rsp,0x10
  0x0000000002524748: pop    rbp
  0x0000000002524749: test   DWORD PTR [rip+0xfffffffffde1b8b1],eax        # 0x0000000000340000
                                                ;   {poll_return}
  0x000000000252474f: ret    

43voto

Vishal K Puntos 9232

Interruptor caso es más rápido si el caso los valores se colocan en un rango estrecho, por ejemplo.

case 1:
case 2:
case 3:
..
..
case n:

Porque, en este caso, el compilador puede evitar la realización de una comparación para cada caso en la pierna en la instrucción switch. El compilador de hacer un salto de la tabla que contiene las direcciones de las acciones a ser tomadas en distintos piernas. El valor en el que el interruptor se realiza son manipulados para convertirlo en un índice en el jump table. En esta implementación , el tiempo empleado en la instrucción switch es mucho menor que el tiempo que se toma en un equivalente a if-else-if declaración de la cascada. También el tiempo dedicado a la instrucción switch es independiente del número de casos de piernas en la instrucción switch.

Como se dice en wikipedia sobre la instrucción switch en la Compilación de la sección.

Si el rango de valores de entrada es identificable 'pequeño' y sólo tiene un pocos huecos, algunos compiladores que incorporan un optimizador que en realidad implementar la instrucción switch como una rama de la tabla o matriz de indizada punteros de función en lugar de una larga serie de condicionales instrucciones. Esto permite que el switch para determinar al instante ¿qué rama para ejecutar sin tener que ir a través de una lista de comparaciones.

27voto

Vivin Paliath Puntos 40975

La respuesta se encuentra en el código de bytes:

SwitchTest10.java

public class SwitchTest10 {

    public static void main(String[] args) {
        int n = 0;

        switcher(n);
    }

    public static void switcher(int n) {
        switch(n) {
            case 0: System.out.println(0);
                    break;

            case 1: System.out.println(1);
                    break;

            case 2: System.out.println(2);
                    break;

            case 3: System.out.println(3);
                    break;

            case 4: System.out.println(4);
                    break;

            case 5: System.out.println(5);
                    break;

            case 6: System.out.println(6);
                    break;

            case 7: System.out.println(7);
                    break;

            case 8: System.out.println(8);
                    break;

            case 9: System.out.println(9);
                    break;

            case 10: System.out.println(10);
                    break;

            default: System.out.println("test");
        }
    }       
}

Correspondiente código de bytes; sólo las partes pertinentes de la ilustración:

public static void switcher(int);
  Code:
   0:   iload_0
   1:   tableswitch{ //0 to 10
        0: 60;
        1: 70;
        2: 80;
        3: 90;
        4: 100;
        5: 110;
        6: 120;
        7: 131;
        8: 142;
        9: 153;
        10: 164;
        default: 175 }

SwitchTest22.java:

public class SwitchTest22 {

    public static void main(String[] args) {
        int n = 0;

        switcher(n);
    }

    public static void switcher(int n) {
        switch(n) {
            case 0: System.out.println(0);
                    break;

            case 1: System.out.println(1);
                    break;

            case 2: System.out.println(2);
                    break;

            case 3: System.out.println(3);
                    break;

            case 4: System.out.println(4);
                    break;

            case 5: System.out.println(5);
                    break;

            case 6: System.out.println(6);
                    break;

            case 7: System.out.println(7);
                    break;

            case 8: System.out.println(8);
                    break;

            case 9: System.out.println(9);
                    break;

            case 100: System.out.println(10);
                    break;

            case 110: System.out.println(10);
                    break;
            case 120: System.out.println(10);
                    break;
            case 130: System.out.println(10);
                    break;
            case 140: System.out.println(10);
                    break;
            case 150: System.out.println(10);
                    break;
            case 160: System.out.println(10);
                    break;
            case 170: System.out.println(10);
                    break;
            case 180: System.out.println(10);
                    break;
            case 190: System.out.println(10);
                    break;
            case 200: System.out.println(10);
                    break;
            case 210: System.out.println(10);
                    break;

            case 220: System.out.println(10);
                    break;

            default: System.out.println("test");
        }
    }       
}

Correspondiente código de bytes; de nuevo, sólo las partes pertinentes de la ilustración:

public static void switcher(int);
  Code:
   0:   iload_0
   1:   lookupswitch{ //23
        0: 196;
        1: 206;
        2: 216;
        3: 226;
        4: 236;
        5: 246;
        6: 256;
        7: 267;
        8: 278;
        9: 289;
        100: 300;
        110: 311;
        120: 322;
        130: 333;
        140: 344;
        150: 355;
        160: 366;
        170: 377;
        180: 388;
        190: 399;
        200: 410;
        210: 421;
        220: 432;
        default: 443 }

En el primer caso, con rangos estrechos, el bytecode compilado utiliza un tableswitch. En el segundo caso, el bytecode compilado utiliza un lookupswitch.

En tableswitch, el valor de entero en la parte superior de la pila se usa para el índice en la tabla, para encontrar la rama/destino de salto. Este saltar de rama a continuación, se realiza de inmediato. Por lo tanto, este es un O(1) de la operación.

Un lookupswitch es más complicado. En este caso, el valor entero necesita ser comparada en contra de todas las llaves en la mesa hasta la clave correcta se encuentra. Después de que la clave se encuentra, la rama/destino de salto (que esta clave es asignada a) se utiliza para el salto. La tabla que se utiliza en lookupswitch está ordenada y un binario de búsqueda algoritmo puede ser utilizado para encontrar la clave correcta. El rendimiento de una búsqueda binaria es O(log n), y todo el proceso es también O(log n), porque el salto es todavía O(1). Así que la razón por la que el rendimiento es menor en el caso de escasa rangos es que la clave correcta debe ser primero miró hacia arriba, porque no índice en la tabla directamente.

Si no son escasos los valores y sólo tenía tableswitch de uso, la tabla contiene esencialmente ficticio entradas que apuntan a las default opción. Por ejemplo, suponiendo que la última entrada en SwitchTest10.java se 21 en lugar de 10, se obtiene:

public static void switcher(int);
  Code:
   0:   iload_0
   1:   tableswitch{ //0 to 21
        0: 104;
        1: 114;
        2: 124;
        3: 134;
        4: 144;
        5: 154;
        6: 164;
        7: 175;
        8: 186;
        9: 197;
        10: 219;
        11: 219;
        12: 219;
        13: 219;
        14: 219;
        15: 219;
        16: 219;
        17: 219;
        18: 219;
        19: 219;
        20: 219;
        21: 208;
        default: 219 }

Por lo que el compilador básicamente crea esta gran tabla que contiene entradas ficticias entre las lagunas, que apunta a la rama de destino de la default instrucción. Incluso si no hay un default, que contendrá las entradas que apuntan a la instrucción después de que el interruptor de bloque. Hice algunas pruebas básicas, y he encontrado que si la brecha entre el índice anterior y el anterior (9) es mayor que 35, se utiliza un lookupswitch en lugar de un tableswitch.

El comportamiento de la switch instrucción se define en la Especificación de la Máquina Virtual de Java (§3.10):

Donde los casos de los switch son escasos, la mesa de la representación de la tableswitch la instrucción se convierte en ineficiente en términos de espacio. El lookupswitch instrucción puede utilizarse en su lugar. El lookupswitch instrucción pares int claves (los valores del caso etiquetas) con el objetivo compensaciones en una tabla. Cuando un lookupswitch instrucción se ejecuta, el valor de la expresión de que el interruptor se compara con las llaves en la mesa. Si una de las claves coincide con el valor de la expresión, la ejecución continúa en el destino asociado a la del offset. Si no hay partidos clave, la ejecución continúa en el destino por defecto. [...]

17voto

bestsss Puntos 6403

Dado que la pregunta ya está contestada (más o menos), he aquí algunas de la punta. Uso

private static final double[] mul={1d, 10d...};
static double multiplyByPowerOfTen(final double d, final int exponent) {
      if (exponent<0 || exponent>=mul.length) throw new ParseException();//or just leave the IOOBE be
      return mul[exponent]*d;
}

Que el código que utiliza significativamente menos IC (caché de instrucciones) y estará siempre en línea. La matriz será en L1 caché de datos si el código está caliente. La tabla de búsqueda es casi siempre una victoria. (esp. en microbenchmarks :D )

Edit: si usted desea que el método hot-entre líneas, considere la posibilidad de la no-rápido senderos, como throw new ParseException() ser tan corto como mínimo o mover a separar el método estático (por lo tanto, haciendo de ellos a corto como mínimo). Que es throw new ParseException("Unhandled power of ten " + power, 0); es una débil idea b/c se come una gran cantidad de las alineaciones de presupuesto para el código que puede ser interpretado - de la concatenación de cadenas es bastante detallado en bytecode . Más info y un caso real w/ ArrayList

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X