234 votos

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.

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