2013-07-16 8 views
14

rozważa następujące programy testowe:Dlaczego dereferencje powodują, że mój program jest szybszy?

Loop value on the stack

int main(void) { 
    int iterations = 1000000000; 

    while (iterations > 0) 
     -- iterations; 
} 

Loop value on the stack (dereferenced)

int main(void) { 
    int iterations = 1000000000; 
    int * p = & iterations; 

    while (* p > 0) 
     -- * p; 
} 

Loop value on the heap

#include <stdlib.h> 

int main(void) { 
    int * p = malloc(sizeof(int)); 
    * p = 1000000000; 

    while (*p > 0) 
     -- * p; 
} 

Przez zestawiania ich ze -O0, mam t on następujące czasy wykonania:

case1.c 
real 0m2.698s 
user 0m2.690s 
sys  0m0.003s 

case2.c 
real 0m2.574s 
user 0m2.567s 
sys  0m0.000s 

case3.c 
real 0m2.566s 
user 0m2.560s 
sys  0m0.000s 

[edytuj] Poniżej znajduje się średnio na 10 egzekucji:

case1.c 
2.70364 

case2.c 
2.57091 

case3.c 
2.57000 

Dlaczego czas realizacji większy od pierwszego przypadku testowego, który wydaje się być najprostszą?

Moja obecna architektura to wirtualna maszyna x86 (Archlinux). Otrzymuję te wyniki zarówno z gcc (4.8.0) i clang (3.3).

[edytuj 1] Wygenerowane kody asemblera są prawie identyczne, z wyjątkiem tego, że drugi i trzeci mają więcej instrukcji niż pierwszy.

[edycja 2] Te wykonania są odtwarzalne (w moim systemie). Każde wykonanie będzie miało ten sam rząd wielkości.

[edytuj 3] Nie interesuje mnie wykonanie niezoptymalizowanego programu, ale nie rozumiem, dlaczego byłby wolniejszy i jestem ciekawy.

+9

Czy próbowałeś spojrzeć na wygenerowany kod? Dlaczego tak czy inaczej zależy Ci na wydajności niezoptymalizowanego kodu? –

+8

Hae próbowałeś je uruchomić w innej kolejności? Czy są to jednorazowe czasy, czy średnie na znacznej liczbie przebiegów? – EJP

+0

@CarlNorum Prawie ten sam wygenerowany kod, z wyjątkiem tego, że w dwóch ostatnich przykładach jest więcej instrukcji (przenoszenie i ładowanie). Nie interesują mnie występy, ale nadal jestem ciekawy :) –

Odpowiedz

6

Trudno powiedzieć, czy to jest powód, ponieważ robię zgadywanie i nie podałeś pewnych szczegółów (na przykład, którego celu używasz). Ale to, co widzę, kiedy mogę skompilować bez optimziations posiadających x86 jest następujące sekwencje decrementign się iterations zmiennej:

Przypadek 1:

L3: 
    sub DWORD PTR [esp+12], 1 
L2: 
    cmp DWORD PTR [esp+12], 0 
    jg L3 

Przypadek 2:

L3: 
    mov eax, DWORD PTR [esp+12] 
    mov eax, DWORD PTR [eax] 
    lea edx, [eax-1] 
    mov eax, DWORD PTR [esp+12] 
    mov DWORD PTR [eax], edx 
L2: 
    mov eax, DWORD PTR [esp+12] 
    mov eax, DWORD PTR [eax] 
    test eax, eax 
    jg L3 

Jedna duża różnica w przypadku 1 widać, że instrukcja pod numerem L3 odczytuje i zapisuje lokalizację pamięci. Następuje natychmiastowa instrukcja, która odczytuje to samo miejsce pamięci, które zostało właśnie napisane. Ten rodzaj sekwencji instrukcji (ta sama lokalizacja pamięci, która została natychmiast zastosowana w następnej instrukcji) często powoduje pewien rodzaj przeciągnięcia potoku w nowoczesnych procesorach.

Będziesz pamiętać, że zapis zaraz potem czytamy o tym samym miejscu nie występuje w przypadku 2.

Again - ta odpowiedź jest trochę świadomej spekulacji.

+0

Czy przetestowałeś swój, ponieważ twój kod asm jest inny niż OP-owy – aaronman

+0

OP wydaje się być tym z Clanga. Mam to samo z gcc 4.8. – snf

+2

@ Czy prawdą jest wątpliwość, czy to pytanie będzie miało odpowiedź wystarczająco interesującą, aby ktokolwiek mógł być zadowolony z – aaronman

Powiązane problemy