¿Qué es microbenchmarking?

He escuchado que se usa este término, pero no estoy del todo seguro de lo que significa, así que:

  • ¿Qué significa y qué NO significa?
  • ¿Cuáles son algunos ejemplos de lo que IS y IS NOT son microbenchmarking?
  • ¿Cuáles son los peligros de microbenchmarking y cómo lo evitas?
    • (¿o es algo bueno?)

Significa exactamente lo que dice en la lata: mide el rendimiento de algo “pequeño”, como una llamada del sistema al kernel de un sistema operativo.

El peligro es que las personas puedan usar cualquier resultado que obtengan de microbenchmarking para dictar optimizaciones. Y como todos sabemos:

Deberíamos olvidarnos de las pequeñas eficiencias, digamos el 97% del tiempo: la optimización prematura es la raíz de todo mal “- Donald Knuth

Puede haber muchos factores que sesgan el resultado de microbenchmarks. Las optimizaciones del comstackdor es uno de ellos. Si la operación que se está midiendo toma tan poco tiempo que cualquier cosa que use para medirla lleva más tiempo que la operación real misma, sus microbenchmarks también estarán sesgados.

Por ejemplo, alguien podría tomar un microbenchmark de la sobrecarga de los bucles for :

 void TestForLoop() { time start = GetTime(); for(int i = 0; i < 1000000000; ++i) { } time elapsed = GetTime() - start; time elapsedPerIteration = elapsed / 1000000000; printf("Time elapsed for each iteration: %d\n", elapsedPerIteration); } 

Obviamente, los comstackdores pueden ver que el ciclo no hace absolutamente nada y no generan ningún código para el ciclo. Entonces, el valor de elapsedPerIteration elapsed y elapsed es bastante inútil.

Incluso si el ciclo hace algo:

 void TestForLoop() { int sum = 0; time start = GetTime(); for(int i = 0; i < 1000000000; ++i) { ++sum; } time elapsed = GetTime() - start; time elapsedPerIteration = elapsed / 1000000000; printf("Time elapsed for each iteration: %d\n", elapsedPerIteration); } 

El comstackdor puede ver que la sum variable no va a usarse para nada y optimizarla, y optimizar el bucle for también. ¡Pero espera! ¿Qué pasa si hacemos esto?

 void TestForLoop() { int sum = 0; time start = GetTime(); for(int i = 0; i < 1000000000; ++i) { ++sum; } time elapsed = GetTime() - start; time elapsedPerIteration = elapsed / 1000000000; printf("Time elapsed for each iteration: %d\n", elapsedPerIteration); printf("Sum: %d\n", sum); // Added } 

El comstackdor puede ser lo suficientemente inteligente como para darse cuenta de que la sum siempre será un valor constante, y optimizar todo eso también. Muchos se sorprenderían de las capacidades de optimización de los comstackdores en estos días.

Pero, ¿qué pasa con las cosas que los comstackdores no pueden optimizar?

 void TestFileOpenPerformance() { FILE* file = NULL; time start = GetTime(); for(int i = 0; i < 1000000000; ++i) { file = fopen("testfile.dat"); fclose(file); } time elapsed = GetTime() - start; time elapsedPerIteration = elapsed / 1000000000; printf("Time elapsed for each file open: %d\n", elapsedPerIteration); } 

¡Incluso esta no es una prueba útil! El sistema operativo puede ver que el archivo se abre con mucha frecuencia, por lo que puede precargarlo en la memoria para mejorar el rendimiento. Prácticamente todos los sistemas operativos hacen esto. Lo mismo ocurre cuando abre aplicaciones: los sistemas operativos pueden descubrir las 5 aplicaciones principales que más abre y precargar el código de la aplicación en la memoria cuando inicia la computadora.

De hecho, hay innumerables variables que entran en juego: localidad de referencia (por ejemplo, matrices vs. listas enlazadas), efectos de cachés y ancho de banda de memoria, comstackción del comstackdor, implementación del comstackdor, comstackdores, cantidad de núcleos de procesador, optimizaciones a nivel de procesador , progtwigdores del sistema operativo, procesos en segundo plano del sistema operativo, etc.

Entonces, microbanchmarking no es exactamente una medida útil en muchos casos. Definitivamente no reemplaza los puntos de referencia de todo el progtwig con casos de prueba bien definidos (perfilado). Primero, escriba un código legible y luego un perfil para ver lo que se debe hacer, si corresponde.

Me gustaría enfatizar que los microbenchmarks no son per se en sí , pero uno tiene que usarlos con cuidado (eso es cierto para muchas otras cosas relacionadas con las computadoras)

No existe una definición de micro-benchmarking, pero cuando lo uso me refiero a un pequeño punto de referencia artificial diseñado para probar el rendimiento de algún hardware específico 1 o función de idioma. Por el contrario, un mejor punto de referencia es un progtwig real diseñado para realizar una tarea real. (Dibujar una línea dura entre los dos casos es inútil, IMO, y no lo intentaré).

Los peligros del micro benchmarking es que es fácil escribir un punto de referencia que arroje resultados totalmente engañosos. Algunas trampas comunes en los micro-puntos de referencia de Java son:

  • escribir código que el comstackdor puede deducir no es un trabajo útil y, por lo tanto, optimizar completamente,
  • sin tener en cuenta la naturaleza “grumosa” de la gestión de memoria de Java, y
  • no tener en cuenta los efectos de inicio de JVM; por ejemplo, la toma de tiempo para cargar y las clases de comstackción JIT, y (a la inversa) la aceleración de la ejecución que ocurre una vez que los métodos se han comstackdo JIT.

Sin embargo, incluso una vez que haya abordado los problemas anteriores, existe un problema sistémico con la evaluación comparativa que es imposible de abordar. El código y el comportamiento de un punto de referencia generalmente tienen poca relación con lo que realmente te importa; es decir, cómo va a funcionar su aplicación. Hay demasiadas “variables ocultas” para que pueda generalizar desde un punto de referencia a progtwigs típicos, y mucho menos a su progtwig.

Por estas razones, aconsejamos regularmente a las personas que NO pierdan su tiempo con micro-benchmarks. En cambio, es mejor escribir un código simple y natural, y usar un generador de perfiles para identificar las áreas que necesitan ser optimizadas a mano. Curiosamente, generalmente los problemas de rendimiento más importantes en aplicaciones reales se deben a un mal diseño de estructuras de datos y algoritmos (incluidos problemas de red, bases de datos y subprocesos) más que al tipo de cosas que los micro-benchmarks típicos intentan. prueba.

@BalusC ha proporcionado un excelente enlace al material sobre este tema en la página de Preguntas Frecuentes de Hotspot . Y aquí hay un enlace a un documento técnico de IBM por Brian Goetz .


1 – Los expertos ni siquiera intentarían hacer benchmarking de hardware en Java. Hay demasiadas “cosas complejas” que suceden entre los códigos de bytes y el hardware para extraer conclusiones válidas / útiles sobre el hardware a partir de los resultados brutos. Sería mejor usar un lenguaje más cercano al hardware; por ejemplo, C o incluso el código de ensamblaje.

  • ¿Qué significa y qué NO significa?

Yo diría que el micro-benchmarking simplemente significa medir algo pequeño. Tiny es probablemente dependiente del contexto, pero por lo general en el nivel de una sola llamada al sistema o algo similar. Benchmarking se refiere a todo lo anterior.

  • ¿Cuáles son algunos ejemplos de lo que IS y IS NOT son microbenchmarking?

Este artículo enumera el tiempo de medición de una llamada al sistema getpid () y la medición del tiempo para copiar memoria utilizando memcpy () como ejemplos de micro-benchmarking.

Cualquier medición de la implementación de un algoritmo, etc., no contaría como micro-evaluación comparativa. Especialmente los informes de resultados que enumeran tareas con tiempos de ejecución decrecientes probablemente rara vez se cuentan como micro benchmarking.

  • ¿Cuáles son los peligros de microbenchmarking y cómo lo evitas?

El peligro obvio es que tienta a los desarrolladores a optimizar las partes incorrectas de un progtwig. Otro peligro es que es notoriamente difícil hacer mediciones de algo pequeño con precisión. La forma más fácil de evitarlo es probablemente obtener una buena idea de dónde se gasta más tiempo en el progtwig.

La gente suele decir “no hagas micro-benchmarking”, pero lo que probablemente quieran decir es “no tomes decisiones de optimización basadas en micro-benchmarks”.

  • (¿o es algo bueno?)

No es malo en sí mismo como otros aquí, y muchas páginas web parecen sugerir. Tiene sus lugares. Trabajo con la reescritura de progtwigs y el tejido de aspecto de tiempo de ejecución, etc. Normalmente publicamos micro-referencias de nuestras instrucciones adicionales, no para guiar ninguna optimización, sino para asegurarnos de que nuestro código adicional no tenga ningún impacto en la ejecución del progtwig reescrito.

Sin embargo, es un arte, especialmente en el contexto de una VM que tiene JIT, tiempos de calentamiento, etc. Aquí se describe un enfoque bien descrito para Java.

Aquí hay algunos buenos artículos de Brian Goetz que explican por qué (micro) el benchmarking es especialmente difícil en Java:

  • Comstackción dinámica y medición del rendimiento
  • Anatomía de un microbenchmark defectuoso
  • Recolección de basura y rendimiento

en el libro (Java Performance The Definitive Guide) tiene esta definición y ejemplo sobre microbenchmarks

  1. Microbenchmarks

    Un microbenchmark es una prueba diseñada para medir el rendimiento de una unidad muy pequeña: el tiempo para llamar a un método sincronizado versus un método no sincronizado; la sobrecarga en la creación de un hilo frente a un grupo de subprocesos; tiempo para ejecutar un algoritmo aritmético versus una implementación alternativa; y así.

    Microbenchmarks puede parecer una buena idea, pero son muy difíciles de escribir correctamente. Considérese el siguiente código, que es un bash de escribir un mocrobenchmark que pruebe la permutación de la implementación diferente de un método para calcular el número 50 de Fibonaccio:

 public void doTest(){ double l; long then = System.currentTimeMillis(); for(int i = 0; i < nLoops; i++){ l = fibImpl1(50); } long now = system.currentTimeMillis(); System.out.println("Elapsed time: " + (now - then)) } ... private double fibImpl1(int n){ if(n < 0) throw new IllegalArgumentException("Must be > 0"); if(n == 0) return 0d; if(n == 1) return 1d; double d = fibImpl1(n - 2) + fibImpl(n - 1); if(Double.isInfinited(d)) throw new ArithmeticException("Overflow"); return d; } 

Microbenchmarks debe usar sus resultados.

El mayor problema con este código es que nunca cambia ningún estado de progtwig. Debido a que nunca se utiliza el resultado del cálculo de Fibonacci, el comstackdor puede descartar ese cálculo, un comstackdor inteligente (incluidos los comstackdores actuales de Java 7 y 8).

terminará ejecutando este código:

 long then = System.currentTimeMillis(); long now = System.currentTimeMillis(); System.out.println("Elapsed time: " + (now - then)); 

Como resultado, el tiempo transcurrido será de unos pocos milisegundos, independientemente de la implementación del método de Fibonacci, o del número de veces que se supone que se debe ejecutar el ciclo.

Hay una forma de evitar ese problema en particular: asegúrese de que cada resultado sea leído, o simplemente no sea escrito. En la práctica, cambiar la definición de l de una variable local a una variable de instancia (declarada con la palabra clave volátil) permitirá medir el rendimiento del método.

Micro Benchmarking es una evaluación comparativa. No creo que valga la pena. La evaluación comparativa efectiva es una evaluación comparativa, creo que vale la pena el tiempo.

En términos generales, microbenchmarking es (como in silico dice) el bash de medir el rendimiento de una tarea muy granular, que es difícil de hacer bien y generalmente inútil en el contexto de los dolores de cabeza reales de rendimiento.