2015-12-30 13 views
5

Ich prüfe, welche Art von Beschleunigung ich von der Verwendung von SIMD-Anweisungen mit RyuJIT bekommen kann und ich sehe einige Disassemblierungsanweisungen, die ich nicht erwarte. Ich stütze den Code auf this blog post von Kevin Frei des RyuJIT-Teams und einen verwandten Posten here. Hier ist die Funktion:Was sind diese zusätzlichen Demontageanweisungen bei der Verwendung von SIMD-Eigen- schaften?

static void AddPointwiseSimd(float[] a, float[] b) { 
    int simdLength = Vector<float>.Count; 
    int i = 0; 
    for (i = 0; i < a.Length - simdLength; i += simdLength) { 
     Vector<float> va = new Vector<float>(a, i); 
     Vector<float> vb = new Vector<float>(b, i); 
     va += vb; 
     va.CopyTo(a, i); 
    } 
} 

Der Abschnitt der Demontage bin ich kopiert die Array-Werte in die Vector<float> abfragt. Die meisten der Demontage ist ähnlich dem in Kevin und Sasha Beiträge, aber ich habe einige zusätzliche Anweisungen hervorgehoben (zusammen mit meinem verwirrten Anmerkungen), die in ihrer Demontagen nicht erscheinen:

;// Vector<float> va = new Vector<float>(a, i); 
    cmp eax,r8d    ; <-- Unexpected - Compare a.Length to i? 
    jae 00007FFB17DB6D5F  ; <-- Unexpected - Jump to range check failure 
    lea r10d,[rax+3] 
    cmp r10d,r8d 
    jae 00007FFB17DB6D5F 
    mov r11,rcx    ; <-- Unexpected - Extra register copy? 
    movups xmm0,xmmword ptr [r11+rax*4+10h ] 

;// Vector<float> vb = new Vector<float>(b, i); 
    cmp eax,r9d    ; <-- Unexpected - Compare b.Length to i? 
    jae 00007FFB17DB6D5F  ; <-- Unexpected - Jump to range check failure 
    cmp r10d,r9d 
    jae 00007FFB17DB6D5F 
    movups xmm1,xmmword ptr [rdx+rax*4+10h] 

Hinweis der Schleifenbereichsprüfung wie erwartet:

;// for (i = 0; i < a.Length - simdLength; i += simdLength) { 
    add eax,4 
    cmp r9d,eax 
    jg loop 

, damit ich weiß nicht, warum es gibt zusätzliche Vergleiche zu eax. Kann jemand erklären, warum ich diese zusätzlichen Anweisungen sehe und ob es möglich ist, sie loszuwerden.

Im Fall ist es im Zusammenhang mit den Projekteinstellungen ich ein sehr ähnliches Projekt habe, das das gleiche Problem zeigt here on github (siehe FloatSimdProcessor.HwAcceleratedSumInPlace() oder UShortSimdProcessor.HwAcceleratedSumInPlaceUnchecked()).

Antwort

10

Ich werde die Code-Generierung mit Anmerkungen versehen, die ich sehe, für einen Prozessor, AVX2 wie Haswell unterstützt, kann es 8 Schwimmer in einer Zeit bewegen:

00007FFA1ECD4E20 push  rsi 
00007FFA1ECD4E21 sub   rsp,20h 

00007FFA1ECD4E25 xor   eax,eax      ; i = 0 
00007FFA1ECD4E27 mov   r8d,dword ptr [rcx+8]   ; a.Length 
00007FFA1ECD4E2B lea   r9d,[r8-8]     ; a.Length - simdLength 
00007FFA1ECD4E2F test  r9d,r9d      ; if (i >= a.Length - simdLength) 
00007FFA1ECD4E32 jle   00007FFA1ECD4E75    ; then skip loop 

00007FFA1ECD4E34 mov   r10d,dword ptr [rdx+8]  ; b.Length 
00007FFA1ECD4E38 cmp   eax,r8d      ; if (i >= a.Length) 
00007FFA1ECD4E3B jae   00007FFA1ECD4E7B    ; then OutOfRangeException 
00007FFA1ECD4E3D lea   r11d,[rax+7]     ; i+7 
00007FFA1ECD4E41 cmp   r11d,r8d      ; if (i+7 >= a.Length) 
00007FFA1ECD4E44 jae   00007FFA1ECD4E7B    ; then OutOfRangeException 

00007FFA1ECD4E46 mov   rsi,rcx      ; move a[i..i+7] 
00007FFA1ECD4E49 vmovupd  ymm0,ymmword ptr [rsi+rax*4+10h] 

00007FFA1ECD4E50 cmp   eax,r10d      ; same as above 
00007FFA1ECD4E53 jae   00007FFA1ECD4E7B    ; but for b 
00007FFA1ECD4E55 cmp   r11d,r10d 
00007FFA1ECD4E58 jae   00007FFA1ECD4E7B 
00007FFA1ECD4E5A vmovupd  ymm1,ymmword ptr [rdx+rax*4+10h] 

00007FFA1ECD4E61 vaddps  ymm0,ymm0,ymm1    ; a[i..] + b[i...] 
00007FFA1ECD4E66 vmovupd  ymmword ptr [rsi+rax*4+10h],ymm0 

00007FFA1ECD4E6D add   eax,8       ; i += 8 
00007FFA1ECD4E70 cmp   r9d,eax      ; if (i < a.Length) 
00007FFA1ECD4E73 jg   00007FFA1ECD4E38    ; then loop 

00007FFA1ECD4E75 add   rsp,20h 
00007FFA1ECD4E79 pop   rsi 
00007FFA1ECD4E7A ret 

So ist die EAX vergleicht diese „lästigen gebundenen Kontrollen "Davon spricht der Blogbeitrag. Der Blog-Post gibt eine optimierte Version, die (noch) nicht implementiert ist, realer Code überprüft jetzt sowohl den ersten als auch den letzten Index der 8 Floats, die zur selben Zeit verschoben werden. Der Kommentar des Blogposts "Hoffentlich werden wir unsere Grenzen - Überprüfung Beseitigung Arbeit genug gestärkt" ist eine unvollständige Aufgabe :)

Die mov rsi,rcx Anweisung ist in der Blog-Post als auch vorhanden und scheint eine Einschränkung im Register zu sein Zuweiser. Wahrscheinlich beeinflusst durch RCX, das ein wichtiges Register ist, speichert es normalerweise dieses. Nicht wichtig genug, um die Arbeit zu erledigen, um diese Optimierung zu vermeiden, würde ich annehmen, dass Register-zu-Register-Bewegungen 0 Zyklen dauern, da sie nur die Registerumbenennung betreffen.

Beachten Sie, wie der Unterschied zwischen SSE2 und AVX2 ist hässlich, während der Code bewegt und fügt 8 Schwimmer auf einmal, es verwendet nur 4 von ihnen. Vector<float>.Count ist 4 unabhängig von der Prozessor-Geschmack, so dass 2x perf auf dem Tisch. Es ist schwer, das Implementierungsdetail zu verstecken.