5

Stavo esaminando un codice che utilizza le flag /fp:precise e /fp:fast.Strange/fp Comportamento flag modello floating point

Secondo la MSDN documentation per /fp:precise:

Con/fp: precise su processori x86, il compilatore eseguirà arrotondamento su variabili di tipo float alla corretta precisione per le assegnazioni e calchi e quando passa parametri a un funzione. Questo arrotondamento garantisce che i dati non mantengano alcun significato superiore alla capacità del suo tipo. Un programma compilato con/fp: preciso può essere più lento e più grande di uno compilato senza/fp: preciso./fp: preciso disabilita gli intrinseci; vengono invece utilizzate le routine di libreria di runtime standard. Per ulteriori informazioni, vedere/Oi (Genera funzioni intrinseche).

Guardando lo smontaggio di una chiamata a sqrtf (chiamato con /arch:SSE2, bersaglio x86/Win32 piattaforma):

0033185D cvtss2sd xmm0,xmm1 
00331861 call  __libm_sse2_sqrt_precise (0333370h) 
00331866 cvtsd2ss xmm0,xmm0 

Da this question credo moderni processori x86/x64 non utilizzano registri 80-bit (o almeno scoraggiare il loro uso), quindi il compilatore fa quello che suppongo sia la cosa migliore e fare calcoli con i doppi a 64 bit. E poiché gli elementi intrinseci sono disabilitati, c'è una chiamata alla funzione sqrtf della libreria.

Ok, abbastanza giusto questo sembra essere conforme a ciò che dice la documentazione.

Tuttavia, quando compilo per l'arco x64, accade qualcosa di strano:

000000013F2B199E movups  xmm0,xmm1 
000000013F2B19A1 sqrtps  xmm1,xmm1 
000000013F2B19A4 movups  xmmword ptr [rcx+rax],xmm1 

I calcoli non vengono rilevati con i doppi a 64 bit, e intrinseche vengono utilizzati. Per quello che posso dire, i risultati sono esattamente gli stessi di quando è stato utilizzato il flag /fp:fast.

Perché c'è una discrepanza tra i due? /fp:precise non funziona semplicemente con la piattaforma x64?

Ora, come controllo di integrità ho testato lo stesso codice in VS2010 x86 con /fp:precise e /arch:SSE2. Sorprendentemente, è stato utilizzato l'intrinseco sqrtpd!

00AF14C7 cvtps2pd xmm0,xmm0 
00AF14CA sqrtsd  xmm0,xmm0 
00AF14CE cvtpd2ps xmm0,xmm0 

Cosa sta succedendo qui? Perché VS2010 utilizza intrinsecamente mentre VS2012 chiama una libreria di sistema?

Il test di VS2010 che ha come target la piattaforma x64 ha risultati simili a quelli di VS2012 (/fp:precise sembra essere ignorato).

Non ho accesso a nessuna versione precedente di VS, quindi non posso eseguire alcun test su queste piattaforme.

Per riferimento sto testando in Windows 7 64-bit con un processore Intel i5-m430.

+2

Questo è davvero strano. So per certo che '/ fp: precise' a volte farà sì che il compilatore promuova gli intermedi con maggiore precisione a discrezione. Ma questo non spiega la pura incongruenza qui. – Mysticial

+0

"Da questa domanda credo che l'arco x86 non abbia registri a 80 bit" Vieni di nuovo? –

+0

Sì, una strana espressione. Aggiornato per chiarire la raccomandazione generale contro il loro uso. – helloworld922

risposta

3

Prima di tutto si consiglia di leggere this post di blog davvero buono sulla precisione a virgola mobile intermedia. L'articolo gestisce solo il codice generato dallo studio visuale (ma questo è ciò che la tua domanda è tutta).E ora agli esempi:

0033185D cvtss2sd xmm0,xmm1 
00331861 call  __libm_sse2_sqrt_precise (0333370h) 
00331866 cvtsd2ss xmm0,xmm0 

Questo codice assembler è stato generato con /fp:precise /arch:SSE2 per la piattaforma x86. Secondo lo documentation, il preciso modello a virgola mobile promuove tutti i calcoli per raddoppiare internamente sulla piattaforma x86. Impedisce anche l'uso di elementi intrinseci (penso che tu abbia letto già this information). Quindi il codice inizia con una conversione da float a double seguita da una doppia chiamata sqrt di precisione e infine il risultato viene riconvertito in float.

000000013F2B199E movups  xmm0,xmm1 
000000013F2B19A1 sqrtps  xmm1,xmm1 
000000013F2B19A4 movups  xmmword ptr [rcx+rax],xmm1 

Il secondo esempio è stato compilato per x64 piattaforma (amd64) e questo comporta piattaforma completamente diverso! Secondo la documentazione:

Per motivi di prestazioni, le operazioni intermedie vengono calcolate con la massima precisione di entrambi gli operandi anziché con la massima precisione disponibile.

Quindi le calibrazioni verranno eseguite con una sola precisione interna. Penso che abbiano deciso di utilizzare intrinsecamente il più possibile, quindi la differenza tra /fp:precise e /fp:fast è leggermente più piccola sulla piattaforma x64. Il nuovo comportamento si traduce in un codice più veloce e e dà al programmatore un maggiore controllo su ciò che accade esattamente (sono stati in grado di cambiare le regole del gioco perché i problemi di compatibilità non riguardavano la nuova piattaforma x64). Sfortunatamente, queste modifiche/differenze non sono esplicitamente indicate nella documentazione.

00AF14C7 cvtps2pd xmm0,xmm0 
00AF14CA sqrtsd  xmm0,xmm0 
00AF14CE cvtpd2ps xmm0,xmm0 

Infine, l'ultimo esempio è stato compilato con Visual Studio 2010 compilatore e penso che accidentalmente usato un intrinseco per sqrt quando dovrebbero meglio non avere (almeno per la modalità /fp:precise), ma hanno deciso di cambiare/correggere nuovamente questo comportamento in Visual Studio 2012 (vedere here).