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).
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
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