2017-03-28 6 views
2

Ich habe den folgenden Assembly-Code ausgeführt: (das iteriert 1000 Mal durch ein Array von 10 000 000 Elemente von jeweils 4 Bytes) auf einem Intel Core i7-CPU (mit 32 KB L1 Datencache und 64B L1-Cache-Zeilengröße)Intel Core i7 Prozessor und Cache-Verhalten

main: 
.LFB0: 
    .cfi_startproc 
    mov edx, 1000 
    jmp .L2 
.L3: 
    mov ecx, DWORD PTR v[0+eax*4] 
    add eax, 1 

    cmp eax, 10000000 
    jl .L3 
    sub edx, 1 
    je .L4 
.L2: 
    mov eax, 0 
    jmp .L3 
.L4: 
    mov eax, 0 
    ret 
    .cfi_endproc 

Perf gibt die folgenden Statistiken:

10,135,716,950  L1-dcache-loads 
601,544,266  L1-dcache-load-misses  # 5.93% of all L1-dcache hits 
4.747253821 seconds time elapsed 

das macht total Sinn, weil ich den Zugriff auf 1 000 * 10 000 000 = 10 000 000 000 Elemente im Speicher und die Cache-Zeile ist 64B (mit einem Element im Vektor von 4 B) bedeutet dies eine L1 Cache-Miss bei e sehr 16 Elemente (also etwa 625 000 000 L1 Cache-Misses).

Nun, ich habe "abgerollt" einen Teil der Schleife und der Code ist:

.cfi_startproc 
    mov  edx, 1000 
    jmp  .L2 
.L3: 
    mov  ecx, DWORD PTR v[0+eax*4] 
    mov  ecx, DWORD PTR v[0+eax*4 + 4] 
    mov  ecx, DWORD PTR v[0+eax*4 + 8] 
    mov  ecx, DWORD PTR v[0+eax*4 + 12] 

    add  eax, 4 

    cmp  eax, 2500000 
    jl  .L3 
    sub  edx, 1 
    je  .L4 
.L2: 
    mov  eax, 0 
    jmp  .L3 
.L4: 
    mov  eax, 0 
    ret 
    .cfi_endproc 

Perf wie gibt die folgende Statistik:

2,503,436,639  L1-dcache-loads 
123,835,666  L1-dcache-load-misses  # 4.95% of all L1-dcache hits 
0.629926637 seconds time elapsed 

Ich kann nicht verstehen, warum?

1) Es gibt weniger L1-Cache-Ladevorgänge, da ich auf die gleiche Datenmenge zugreife?

2) Der Code läuft 6 Mal schneller als die erste Version? Ich weiß, dass es mit Out-of-Order-Ausführung und Superscalar-Ausführung zu tun hat, aber ich kann dies nicht im Detail erklären (Ich möchte genau verstehen, was diese Beschleunigung verursacht).

Antwort

4

Bad news - Sie haben einen Fehler in zweiter;)

Originalcode

.L3: 
    mov ecx, DWORD PTR v[0+eax*4] 
    add eax, 1 
    cmp eax, 10000000 
    jl .L3 

Zweite Version

.L3: 
    mov  ecx, DWORD PTR v[0+eax*4] 
    mov  ecx, DWORD PTR v[0+eax*4 + 4] 
    mov  ecx, DWORD PTR v[0+eax*4 + 8] 
    mov  ecx, DWORD PTR v[0+eax*4 + 12] 
    add  eax, 4 
    cmp  eax, 2500000 <- here 
    jl  .L3 

In beiden Fällen müssen Sie 10 Millionen Elemente laden. Die maximale Elementadresse, auf die in beiden Fällen zugegriffen wird, muss gleich sein, richtig?

So in erster Fall max Adresse lautet:

(10.000.000-1)*4 = 39.999.996 

und zweite:

(2.500.000-4)*4+12 = 9.999.996 

genau 4 mal weniger.

Nur zweite Beispiel zu cmp eax, 10000000 zu beheben.

+0

Ok :(Ich habe es behoben. Jetzt ist der Cache-Treffer und Ladevorgänge in Ordnung. Aber es wird noch schneller ausgeführt: 2.617806762 Sekunden. Warum? Es ist nur, weil es weniger Sprunganweisungen zu tun braucht – SebiSebi

+0

Abrollen bringt in der Regel Gewinne aber was passiert, wenn man nicht immer auf ecx lädt, sondern auf verschiedene Register (die ersten 3 Ladungen sind "nicht wirksam", da die letzte immer ecx überschreibt, damit die CPU das sehen und etwas Magie machen kann) Hier überprüfen Sie, was passiert, wenn Sie 8 oder 16 Mal ausrollen. – Anty

Verwandte Themen