2009-02-13 21 views
8

No sé mucho sobre los aspectos internos del compilador y las optimizaciones de JIT, pero generalmente trato de usar el "sentido común" para adivinar qué podría optimizarse y qué no. Así que estaba escribiendo un método de prueba unitaria simple hoy:¿Cómo escribo (prueba) el código que no será optimizado por el compilador/JIT?

@Test // [Test] in C# 
public void testDefaultConstructor() { 
    new MyObject(); 
} 

Este método es en realidad todo lo que necesito. Comprueba que el constructor predeterminado existe y se ejecuta sin excepciones.

Pero luego comencé a pensar en el efecto de las optimizaciones del compilador/JIT. ¿Podría el compilador/JIT optimizar este método eliminando por completo la declaración new MyObject();? Por supuesto, necesitaría determinar que el gráfico de llamadas no tiene efectos secundarios para otros objetos, que es el caso típico de un constructor normal que simplemente inicializa el estado interno del objeto.

Supongo que solo el JIT podría realizar dicha optimización. Esto probablemente significa que no es algo de lo que deba preocuparme, porque el método de prueba se realiza solo una vez. ¿Son mis suposiciones correctas?

Sin embargo, estoy tratando de pensar sobre el tema general. Cuando pensé en cómo evitar que este método se optimice, pensé que podría assertTrue(new MyObject().toString() != null), pero esto depende mucho de la implementación real del método toString(), e incluso entonces, el JIT puede determinar que el método toString() siempre devuelve un método no nulo cadena (por ejemplo, si realmente se está llamando al Object.toString()), y así optimizar toda la rama. Entonces esta forma no funcionaría.

Sé que en C# puedo usar [MethodImpl(MethodImplOptions.NoOptimization)], pero esto no es lo que realmente estoy buscando. Espero encontrar una forma (independiente del lenguaje) de asegurarme de que algunas partes específicas de mi código se ejecutarán realmente como espero, sin que el JIT interfiera en este proceso.

Además, ¿hay algún caso típico de optimización que deba tener en cuenta al crear mis pruebas unitarias?

¡Muchas gracias!

Respuesta

4

No te preocupes. No está permitido optimizar nada que pueda marcar una diferencia para su sistema (excepto la velocidad). Si actualizas un objeto, se llama al código, se asigna la memoria, TIENE que funcionar.

Si lo tenía protegido por un if (falso), donde falso es un final, podría ser optimizado fuera del sistema por completo, entonces podría detectar que el método no hace nada y optimizarlo (en teoría).

Editar: por cierto, también puede ser lo suficientemente inteligente como para determinar que este método:

newIfTrue(boolean b) { 
    if(b) 
     new ThisClass(); 
} 

siempre hará nada si b es falsa, y, finalmente, darse cuenta de que en algún momento de su código B siempre es falso y compila esta rutina fuera de ese código por completo.

Aquí es donde el JIT puede hacer cosas que son prácticamente imposibles en cualquier lenguaje no administrado.

+0

Gracias. Me pregunto por qué el JIT se comporta de esta manera? Si la asignación de un objeto es inútil (como en realidad puede determinarse por análisis estático en algunos casos), ¿por qué el JIT no lo optimizaría? –

+0

Ahora puedo pensar en un caso de esquina, pero creo que es bastante raro.Si la asignación de objetos se realizó, por ejemplo, para garantizar que haya suficiente memoria disponible para algunos otros objetos (e incluso para asegurarse de que no haya paginación), la optimización invalidaría la suposición. –

+0

Se requiere dejar Java Virtual Machine (JVM) en un estado consistente con haber ejecutado el código del programa en la JVM de acuerdo con el Modelo de memoria de Java. No es necesario ejecutar realmente ningún código específico ni asignar memoria en el caso de que el JIT pueda demostrar que el código no tiene ningún efecto en el estado del programa observable. –

5

Creo que si te preocupa que se optimice, es posible que estés haciendo un poco de prueba demasiado.

En un lenguaje estático, suelo pensar en el compilador como una prueba. Si pasa la compilación, eso significa que hay ciertas cosas allí (como los métodos). Si no tiene otra prueba que ejercite su constructor predeterminado (que demostrará que no arrojará excepciones), es posible que desee pensar en por qué está escribiendo ese constructor predeterminado en primer lugar (YAGNI y todo eso).

Sé que hay personas que no están de acuerdo conmigo, pero tengo la sensación de que este tipo de cosas son solo cosas que abotargarán su número de pruebas sin ninguna razón útil, incluso mirándolo con gafas TDD.

+0

Sí, probando overkill ftl. – MichaelGG

+0

Estoy de acuerdo contigo. Cuando lo pensé (después de leer su respuesta), debe haber otras pruebas que usen el constructor predeterminado si esta prueba es más importante que el simple hecho de ser una prueba "compilada". ¡Gracias! –

+0

En un lenguaje como Java, los entornos de compilación y ejecución pueden variar mucho. Una prueba como esta podría tener sentido si la clase que se está construyendo se encuentra separada del código de prueba. Especialmente interesante sería que un cargador de clases personalizado cargara la clase sobre la marcha desde alguna forma de almacenamiento dinámico; este código aseguraría que la búsqueda funcionara realmente. –

0

¿Por qué debería importar? Si el compilador/JIT puede determinar de manera estática que no se va a golpear ninguna de las afirmaciones (lo que podría causar efectos secundarios), entonces estás bien.

+0

También necesita verificar que el constructor existe, no afecta el estado del programa estático, y no puede arrojar una excepción implícitamente requerida por la JVM, como una 'NullPointerException'. –

2

Piénsalo de esta manera:

supongamos que el compilador puede determinar que el gráfico de llamadas no tiene ningún efecto secundario (no creo que es posible, recuerdo vagamente algo sobre P = NP de mis cursos de CS). Optimiza cualquier método que no tenga efectos secundarios. Como la mayoría de las pruebas no tienen y no deberían tener ningún efecto secundario, el compilador puede optimizarlas.

+0

¡Buena idea! No pensé en eso. :) –

1

Parece que en C# que podría hacer esto:

[Test] 
public void testDefaultConstructor() { 
    GC.KeepAlive(new MyObject()); 
} 

AFAIU, el método GC.KeepAlive no va a ser inline por el JIT, por lo que el código se garantiza que funcione como se espera. Sin embargo, no conozco una construcción similar en Java.

+1

-1: Engañoso. Este no es el caso donde se requiere 'GC.KeepAlive' (o incluso proporciona algún beneficio). –

0

Cada I/O es un efecto secundario, por lo que sólo puede poner

Object obj = new MyObject(); 
System.out.println(obj.toString()); 

y que está bien.

+0

Sí, esta ciertamente es una forma de hacerlo. Pero las pruebas unitarias generalmente no deberían tener declaraciones de salida. –

+0

Creo que siempre y cuando no confíe en E/S para determinar si la prueba pasó o no, está bien. –

+1

-1: engañoso. Él está bien sin la E/S. Esta respuesta hace que parezca que las pruebas unitarias de este formulario necesitan la E/S para garantizar la corrección. –

2

El JIT solo puede realizar operaciones que no afecten a la semántica garantizada del idioma. Teóricamente, podría eliminar la asignación y llamar al constructor MyObjectsi puede garantizar que la llamada no tiene efectos secundarios y nunca puede lanzar una excepción (sin contar OutOfMemoryError).

En otras palabras, si el JIT optimiza la llamada de su prueba, entonces su prueba habría pasado de todos modos.

PS: Tenga en cuenta que esto se aplica debido a que está haciendo funcionalidad de pruebas en comparación con el rendimiento pruebas. En las pruebas de rendimiento, es importante asegurarse de que el JIT no optimice la operación que está midiendo, de lo contrario, sus resultados serán inútiles.

+0

Gran vista sobre el tema. Una adición: cuando se usa una prueba de rendimiento para la funcionalidad (por ejemplo, tratando de calcular el tamaño de caché de la CPU), deberíamos agregar explícitamente '[MethodImpl (MethodImplOptions.NoOptimization)]' para evitar que el JIT haga trucos. –

Cuestiones relacionadas