¿Por qué la adición de variables locales causa retraso en el método?

Recientemente he empezado a leer sobre benchmarks y escribirlos para Android (en Java). Soy consciente de problemas tales como calentamientos, recolector de basura y optimizaciones de compilador, pero no sé si el problema que enfrento podría ser causado por cualquiera de ellos.

En mi aplicación de referencia, creo una matriz de 10,000 variables flotantes e inicializa con valores aleatorios. Al ejecutar código de referencia:

private void runMinorBenchmarkFloat (float[] array) { float sum = 0; long startTime; long endTime; /* Fast warm-up */ startTime = System.nanoTime(); for(int i=0; i<SMALL_LOOP_ITERATION_COUNT; i++) for(int j=0; j<TAB_SIZE; j++) sum += array[j]; endTime = System.nanoTime() - startTime; postMessage("Warm-up for FLOAT finished in: " + endTime/1000000 + "ms.\n"); /* Main benchmark loop */ startTime = System.nanoTime(); for(int i=0; i<BIG_LOOP_ITERATION_COUNT; i++) { sum = 0; for(int j=0; j<TAB_SIZE; j++) sum += array[j]; } endTime = System.nanoTime() - startTime; postMessage("Benchmark for FLOAT finished in: " + endTime/1000000 + "ms.\n"); postMessage("Final value: " + sum + "\n\n"); } 

En mi teléfono consigo alrededor 2 segundos para el calentamiento y 20 segundos para el lazo "verdadero".

Ahora, cuando agrego dos variables más flotante (sum2 y sum3 – nunca usado dentro del método):

 private void runMinorBenchmarkFloat (float[] array) { float sum = 0, sum2 = 0, sum3 = 0; // <------- the only code change here!!! long startTime; long endTime; /* Fast warm-up */ startTime = System.nanoTime(); for(int i=0; i<SMALL_LOOP_ITERATION_COUNT; i++) for(int j=0; j<TAB_SIZE; j++) sum += array[j]; endTime = System.nanoTime() - startTime; postMessage("Warm-up for FLOAT finished in: " + endTime/1000000 + "ms.\n"); /* Main benchmark loop */ startTime = System.nanoTime(); for(int i=0; i<BIG_LOOP_ITERATION_COUNT; i++) { sum = 0; for(int j=0; j<TAB_SIZE; j++) sum += array[j]; } endTime = System.nanoTime() - startTime; postMessage("Benchmark for FLOAT finished in: " + endTime/1000000 + "ms.\n"); postMessage("Final value: " + sum + "\n\n"); } 

El tiempo de ejecución salta de 2 segundos para el calentamiento a 5 segundos y de 20 segundos para el bucle real a 50 segundos.

Las constantes:

 SMALL_LOOP_ITERATION_COUNT = 100,000 BIG_LOOP_ITERATION_COUNT = 1,000,000 

¿Crees que tal diferencia podría ser causada por problemas de alineación (solo idea suelta)?

Gracias de antemano por cualquier respuesta.

EDITAR:

Parece que este error no aparece en todos los dispositivos. Puedo reproducirlo en Samsung Galaxy S5. El objetivo principal del programa era hacer poco referencia. Hice cuatro casi las mismas funciones (runMinorBenchmark____ donde _ era: int, short, float, double) que diferían sólo en el tipo variable 'sum'. En la función de referencia principal invocaba esas funciones. Debido a que el mencionado error ocurrió decidí combinar esas funciones en una grande. Ahora … Al ejecutar la prueba tengo tales tiempos: 1. 37640ms. (Para int) 2. 46728ms. (Para abreviar) 3. 60589ms. (Para el flotador) 4. 34467ms. (Para el doble)

Sé que corto se significa para ser más lento debido a la fundición del tipo. También pensé que el flotador debería ser más lento en caso de lanzar a doble (tal vez FPU hace casting de tipo de doble cada vez (?)). Pero cuando cambio el tipo variable para sumFloat del flotador al doble el tiempo para el flotador es idéntico al tiempo doble. También hice este "punto de referencia" en otro dispositivo que parecía no sufrir de este comportamiento extraño y los tiempos para cada prueba eran casi los mismos: ~ 45000ms. (Realmente no hay diferencias visibles).

Error de Dalvik VM (?)

    Me niego a creer que es la causa de su problema. Seguramente el compilador acaba de sacar esas variables no utilizadas? ¿Está seguro de que la matriz de entrada no cambia, o sus constantes, o TAB_SIZE ?

    Si todavía está seguro, probarlo corriendo algo como esto y pegar la salida aquí:

     public void proveIt() { float[] inputArray = new float[10000]; for (int i = 0; i < 10000; i++) { inputArray[i] = 1; } postMessage("Without declaration:"); runMinorBenchmarkFloatA(inputArray); postMessage("With declaration:"); runMinorBenchmarkFloatB(inputArray); postMessage("And again just to make sure..."); postMessage("Without declaration:"); runMinorBenchmarkFloatA(inputArray); postMessage("With declaration:"); runMinorBenchmarkFloatB(inputArray); } long TAB_SIZE = 10000; long SMALL_LOOP_ITERATION_COUNT = 100000; long BIG_LOOP_ITERATION_COUNT = 1000000; private void runMinorBenchmarkFloatA(float[] array) { float sum = 0; long startTime; long endTime; /* Fast warm-up */ startTime = System.nanoTime(); for (int i = 0; i < SMALL_LOOP_ITERATION_COUNT; i++) for (int j = 0; j < TAB_SIZE; j++) sum += array[j]; endTime = System.nanoTime() - startTime; postMessage("Warm-up for FLOAT finished in: " + endTime / 1000000 + "ms.\n"); /* Main benchmark loop */ startTime = System.nanoTime(); for (int i = 0; i < BIG_LOOP_ITERATION_COUNT; i++) { sum = 0; for (int j = 0; j < TAB_SIZE; j++) sum += array[j]; } endTime = System.nanoTime() - startTime; postMessage("Benchmark for FLOAT finished in: " + endTime / 1000000 + "ms.\n"); postMessage("Final value: " + sum + "\n\n"); } private void runMinorBenchmarkFloatB(float[] array) { float sum = 0, sum2 = 0, sum3 = 0; long startTime; long endTime; /* Fast warm-up */ startTime = System.nanoTime(); for (int i = 0; i < SMALL_LOOP_ITERATION_COUNT; i++) for (int j = 0; j < TAB_SIZE; j++) sum += array[j]; endTime = System.nanoTime() - startTime; postMessage("Warm-up for FLOAT finished in: " + endTime / 1000000 + "ms.\n"); /* Main benchmark loop */ startTime = System.nanoTime(); for (int i = 0; i < BIG_LOOP_ITERATION_COUNT; i++) { sum = 0; for (int j = 0; j < TAB_SIZE; j++) sum += array[j]; } endTime = System.nanoTime() - startTime; postMessage("Benchmark for FLOAT finished in: " + endTime / 1000000 + "ms.\n"); postMessage("Final value: " + sum + "\n\n"); } 

    Ahora, cuando agrego dos variables más flotante (sum2 y sum3 – nunca usado dentro del método):

     float sum = 0, sum2 = 0, sum3 = 0; // <------- the only code change here!!! 

    Pero sum2 y sum3 se utilizan dentro del método. Se inicializan a cero. Esto cuesta tiempo.

    Si no se inicializan, el código de byte generado sería idéntico con y sin ellos, aparte del tamaño del marco de pila asignado, lo que no afecta a la temporización.

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.