Naturalmente dipenderà molto dal codice, ma ho implementato due semplici funzioni utilizzando entrambi gli approcci. See code
__m128 calc_set1(float num1, float num2)
{
__m128 num1_4 = _mm_set1_ps(num1);
__m128 num2_4 = _mm_set1_ps(num2);
__m128 result4 = _mm_mul_ps(num1_4, num2_4);
return result4;
}
__m128 calc_mov(float* num1_4_addr, float* num2_4_addr)
{
__m128 num1_4 = _mm_load_ps(num1_4_addr);
__m128 num2_4 = _mm_load_ps(num2_4_addr);
__m128 result4 = _mm_mul_ps(num1_4, num2_4);
return result4;
}
e montaggio
calc_set1(float, float):
shufps $0, %xmm0, %xmm0
shufps $0, %xmm1, %xmm1
mulps %xmm1, %xmm0
ret
calc_mov(float*, float*):
movaps (%rdi), %xmm0
mulps (%rsi), %xmm0
ret
Si può vedere che la calc_mov()
fa come quello che ci si aspetterebbe e la calc_set1()
utilizza una singola istruzione shuffle.
A movps
Un'istruzione può richiedere circa quattro cicli per la generazione di indirizzi e altro se la porta di caricamento della cache L1 è occupata + più nell'evento raro di una mancanza di cache.
shufps
eseguirà un singolo ciclo su una delle recenti microarchitettura Intel. Credo che sia vero sia per SSE128 o AVX256. Pertanto suggerirei di utilizzare l'approccio mm_set1_ps
.
Ovviamente, un'istruzione shuffle presuppone che il float si trovi già in un registro SSE/AVX. Nel caso in cui lo stiate caricando dalla memoria, la trasmissione sarà migliore dato che catturerà il meglio di movps
e shufps
in una singola istruzione.
Quando dici "carica" presumo tu intenda copiando una variabile esistente (registro), giusto? Non dalla memoria? – Mehrdad
@ Mehrdad Beh, principalmente intendo dalla memoria, a meno che non si trovi già nel registro. Per l'opzione precomputa, voglio dire impostare __m128 da qualche parte nel codice e quindi fare riferimento a esso ovunque ne abbiate bisogno, invece di generare lo stesso vettore con _mm_set1_ps. Questa è la spiegazione più semplice. – Thomas
È difficile dire apertamente dato che 'mm_set1_ps' non corrisponde a nessuna singola istruzione. Potrebbe persino tradurre in ciò che stai suggerendo di fare. Perché non controllare l'assemblaggio e vedere? Inoltre, dal momento che hai taggato il tuo post con AVX, ti piacerebbe sapere che c'è una nuova intrinseca che corrisponde a una singola istruzione: '_mm_broadcast_ss' – hayesti