2015-12-30 16 views
5

Sto testando quale tipo di accelerazione posso ottenere usando le istruzioni SIMD con RyuJIT e sto vedendo alcune istruzioni di smontaggio che non mi aspetto. Sto basando il codice su this blog post da Kevin Frei del team RyuJIT e un post correlato here. Ecco la funzione:Quali sono queste istruzioni per lo smontaggio extra quando si utilizzano gli intrinsechi SIMD?

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

La sezione di disassemblaggio Sono interrogazione copia i valori di matrice nella Vector<float>. La maggior parte del smontaggio è simile a quella di Kevin e Sasha dei messaggi, ma ho messo in evidenza alcune istruzioni in più (insieme con i miei annotazioni confuse) che non appaiono nei loro smontaggi:

;// 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] 

Nota Il test di ricezione ciclo è come previsto:

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

quindi non so il motivo per cui non ci sono paragoni extra per eax. Qualcuno può spiegare perché sto vedendo queste istruzioni extra e se è possibile sbarazzarsi di loro.

Nel caso in cui sia correlato alle impostazioni del progetto, ho un progetto molto simile che mostra lo stesso numero here on github (vedere FloatSimdProcessor.HwAcceleratedSumInPlace() o UShortSimdProcessor.HwAcceleratedSumInPlaceUnchecked()).

risposta

10

io annotare il codice generazione che vedo, per un processore che supporti AVX2 come Haswell, può muoversi di 8 carri alla volta:

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 

Così l'eax confronto sono quelli "controlli legati fastidiosi "di cui parla il post del blog. Il post sul blog fornisce una versione ottimizzata che non è ancora implementata (ancora), il codice reale in questo momento controlla sia il primo che l'ultimo indice degli 8 oggetti mobili che vengono spostati contemporaneamente. Il commento del post del blog "Speriamo, otterremo i nostri limiti - verifica che il lavoro di eliminazione sia sufficientemente rafforzato" è un'attività incompleta :)

L'istruzione mov rsi,rcx è presente anche nel post del blog e sembra essere una limitazione nel registro allocatore. Probabilmente influenzato dal fatto che RCX è un registro importante, normalmente memorizza questo. Non abbastanza importante da fare il lavoro per ottenere questo ottimizzato via presumibilmente, le mosse da registro a registro richiedono 0 cicli poiché influenzano solo la rinomina del registro.

Nota come la differenza tra SSE2 e AVX2 è brutta, mentre il codice si sposta e aggiunge 8 caratteri alla volta, in realtà ne utilizza solo 4. Vector<float>.Count è 4 indipendentemente dal sapore del processore, lasciando 2x perf sul tavolo. Difficile nascondere i dettagli di implementazione, immagino.

Problemi correlati