2014-09-16 3 views
8

Recentemente ho iniziato a leggere i benchmark e li scrivo per Android (in Java). Sono a conoscenza di problemi come warmups, garbage collector e ottimizzazioni del compilatore ma non so se il problema che ho di fronte potrebbe essere causato da uno di questi.Perché l'aggiunta di variabili locali causa un ritardo nel metodo?

Nella mia app benchmark creo una matrice di 10.000 variabili float e la inizializzo con valori casuali. Quando si esegue il codice di riferimento:

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"); 
    } 

sul mio telefono ottengo circa 2 secondi per riscaldamento e 20 secondi per ciclo "reale".

Ora, quando aggiungo altri due variabili float (sum2 e SUM3 - mai usato all'interno del metodo):

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"); 
    } 

tempo di esecuzione salta da 2 secondi per riscaldamento a 5 secondi e da 20 secondi per il ciclo vero e proprio a 50 secondi.

Le costanti:

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

Pensi che tale differenza potrebbe essere causato da problemi di allineamento (solo idea sciolto)?

Grazie in anticipo per eventuali risposte.

EDIT:

Sembra che questo errore non viene visualizzato su tutti i dispositivi. Posso riprodurlo su Samsung Galaxy S5. L'obiettivo principale del programma era di fare un piccolo punto di riferimento. Ho eseguito quattro funzioni quasi identiche (runMinorBenchmark____ dove _ era: int, short, float, double) che differivano solo nel tipo di 'somma' variabile. Nella funzione principale di riferimento ho invocato queste funzioni. Poiché si è verificato l'errore menzionato, ho deciso di unire quella funzione in una grande. Ora ... Durante l'esecuzione del test, ho i seguenti orari: 1. 37640 ms. (per int) 2. 46728 ms. (in breve) 3. 60589 ms. (per galleggiante) 4. 34467 ms. (per il doppio)

So che il cortocircuito deve essere più lento a causa del tipo di trasmissione. Ho anche pensato che il float dovrebbe essere più lento in caso di casting per raddoppiare (forse FPU digita casting per raddoppiare ogni volta (?)). Ma quando cambio il tipo di variabile per sumFloat da float a raddoppiare il tempo per float è identico al doppio tempo. Ho anche fatto questo "benchmark" su un altro dispositivo che sembrava non soffrire di questo strano comportamento e i tempi per ogni test erano quasi gli stessi: ~ 45000ms. (davvero nessuna differenza visibile).

errore Dalvik VM (?)

+1

No. L'aggiunta di due campi "non utilizzati" non influirà sul tempo di esecuzione effettivo del ciclo. Qualcos'altro deve essere sbagliato. – TheLostMind

+0

Sono d'accordo con @TheLostMind. L'aggiunta di due variabili float non dovrebbe aumentare significativamente il tempo di esecuzione. Come potete vedere, la CPU può elaborare 10.000 float molto rapidamente, quindi non c'è motivo per cui non dovrebbe essere in grado di elaborare questi due in un modo veloce. – erad

+0

Grazie per le risposte. Sono d'accordo con entrambi, ma è quello che succede davvero. Hai qualche idea su dove guardare oltre? – Waszker

risposta

2

Mi rifiuto di credere che è la causa del problema. Sicuramente il compilatore semplicemente rimuove le variabili inutilizzate? Sei sicuro che l'array di input non cambi, o le tue costanti, o ?

Se siete ancora sicuri, dimostrarlo eseguendo qualcosa di simile e incollando l'output qui:

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"); 
} 
+0

Ok, l'ho fatto su due dispositivi diversi e qui ci sono i risultati: Primo dispositivo, su cui si sono verificati i problemi precedenti (Samsung Galaxy S5): 1. 21170 ms. 2. 55626 ms. 3. 20640 ms. 4. 55522 ms. Ma sul secondo dispositivo (nota Samsung): 1. 32194 ms. 2. 34008 ms. 3. 32171 ms. 4. 34359 ms. – Waszker

0

Ora, quando aggiungo altri due variabili float (sum2 e SUM3 - mai utilizzato all'interno il metodo):

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

Ma sum2 e sum3sono usati all'interno del metodo. Sono inizializzati a zero. Questo costa tempo.

Se non sono stati inizializzati, il codice byte generato sarebbe identico a e senza di essi, a parte la dimensione del frame dello stack allocato, che non influisce sulla temporizzazione.

+1

Penso che non possa causare ritardi così grandi, vero? – Waszker