2014-09-16 14 views
8

Niedawno zacząłem czytać o testach porównawczych i pisać je dla Androida (w Javie). Zdaję sobie sprawę z takich problemów, jak rozgrzewki, garbage collector i optymalizacje kompilatorów, ale nie wiem, czy problem, który napotykam, może być spowodowany przez którykolwiek z tych problemów.Dlaczego dodawanie zmiennych lokalnych powoduje opóźnienie metody?

W mojej aplikacji testowej tworzę tablicę 10 000 zmiennych zmiennoprzecinkowych i inicjalizuję ją losowymi wartościami. Podczas uruchamiania kodu odniesienia:

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

na moim telefonie mam około 2 sekundy do rozgrzewki i 20 sekund do „prawdziwego” pętli.

Teraz, gdy dodam jeszcze dwie zmienne float (sum2 i sum3 - nigdy nie używane wewnątrz metody):

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

czas realizacji skacze z 2 sekund rozgrzewki na 5 sekund i od 20 sekund do realnego pętli do 50 sekund.

Stałe:

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

Myślisz, że taka różnica może być spowodowana przez problemy wyrównania (tylko luźny pomysł)?

Z góry dziękuję za wszelkie odpowiedzi.

EDIT:

Wydaje się, że ten błąd nie pojawia się na każdym urządzeniu. Mogę go odtworzyć na Samsung Galaxy S5. Głównym celem programu było wykonanie małego benchmarku. Zrobiłem cztery prawie te same funkcje (runMinorBenchmark____ gdzie _ było albo: int, short, float, double), które różniły się tylko zmiennym typem "sum". W głównej funkcji testu porównawczego wywołałem te funkcje. Ponieważ wystąpił wspomniany błąd, postanowiłem połączyć te funkcje w jedną dużą. Teraz ... Podczas testu mam takie czasy: 1. 37640ms. (dla int) 2. 46728 ms. (w skrócie) 3. 60589ms. (dla pływaka) 4. 34467ms. (dla podwójnego)

Wiem, że krótki ma być wolniejszy z powodu odlewania typu. Sądziłem również, że pływak powinien być wolniejszy w przypadku rzucania go do podwójnego (być może FPU robi typ castingu za każdym razem dwukrotnie (?)). Ale kiedy zmienię typ zmiennej dla sumFloat z float na podwójny czas dla float jest identyczny z podwójnym czasem. Zrobiłem też ten "benchmark" na innym urządzeniu, które wydawało się nie cierpieć z powodu tego dziwnego zachowania, a czasy każdego testu były prawie takie same: ~ 45000ms. (naprawdę żadnych widocznych różnic).

błąd Dalvik VM (?)

+1

Nie Dodanie dwóch "nieużywanych" pól nie wpłynie na faktyczny czas działania pętli. Coś jeszcze musi być nie tak. – TheLostMind

+0

Zgadzam się z @ TheLostMind. Dodanie dwóch zmiennych zmiennoprzecinkowych nie powinno znacząco wydłużyć czasu wykonania. Jak widać, procesor może przetwarzać 10 000 ruchów bardzo szybko, więc nie ma powodu, dla którego nie byłby w stanie przetworzyć tych dwóch w szybki sposób. – erad

+0

Dzięki za odpowiedzi. Zgadzam się z obojgiem, ale tak właśnie się dzieje. Czy masz jakieś pomysły, gdzie szukać dalej? – Waszker

Odpowiedz

2

Nie wierzę, że jest przyczyną twoich kłopotów. Z pewnością kompilator odciąga te nieużywane zmienne? Jesteś pewny, że tablica wejściowa się nie zmienia, albo twoje stałe, czy ?

Jeśli nadal jesteś pewny, to udowodnić, uruchamiając coś takiego i wklejając wyjście tutaj:

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, zrobiłem to na dwóch różnych urządzeniach i oto wyniki: Pierwsze urządzenie, na którym wystąpiły poprzednie problemy (Samsung Galaxy S5): 1. 21170ms. 2. 55626ms. 3. 20640ms. 4. 55522 ms. Ale na drugim urządzeniu (Uwaga Samsung): 1. 32194ms. 2. 34008 ms. 3. 32171 ms. 4. 34359ms. – Waszker

0

Teraz, gdy dodam jeszcze dwie zmienne float (sum2 i sum3 - nigdy nie używany wewnątrz metoda):

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

Ale sum2 i sum3są wykorzystywane wewnątrz metody. Zostają zainicjowane do zera. To kosztuje czas.

Jeśli nie zostały zainicjalizowane, wygenerowany kod bajtu będzie identyczny z nimi i bez nich, oprócz rozmiaru przydzielonej ramki stosu, co nie ma wpływu na taktowanie.

+1

Myślę, że to nie mogło spowodować tak dużych opóźnień, prawda? – Waszker

Powiązane problemy