2012-12-11 12 views
5

Se si scrive un'applicazione che è molto latenza sensibile quali sono i limiti per l'incorporamento assemblatore all'interno di funzioni C++ (e utilizzando la funzione di C++ chiamate normalmente), in questo modo:L'incorporamento all'interno del C++ è accettabile?

inline __int64 GetCpuClocks() 
{ 

    // Counter 
    struct { int32 low, high; } counter; 

    // Use RDTSC instruction to get clocks count 
    __asm push EAX 
    __asm push EDX 
    __asm __emit 0fh __asm __emit 031h // RDTSC 
    __asm mov counter.low, EAX 
    __asm mov counter.high, EDX 
    __asm pop EDX 
    __asm pop EAX 

    // Return result 
    return *(__int64 *)(&counter); 

} 

(La funzione di cui sopra è venuto da un altro post SO visto)

È possibile trattare funzioni integrate assembler come una scatola nera ? Potresti recuperare facilmente un risultato dai calcoli eseguiti nell'assemblatore? Ci sono pericoli che non sai quali variabili sono attualmente nei registri, ecc.? Fa più problemi che a risolvere, o è accettabile per compiti specifici?

(dove l'architettura sta per essere fissato, e conosciuto)

EDIT Ho appena trovato questo, questo è quello che sto suggerendo:

http://www.codeproject.com/Articles/15971/Using-Inline-Assembly-in-C-C

EDIT2 questo è più rivolto verso Linux e x86 - è solo una domanda generale C++/assemblatore (o almeno così pensavo).

+1

Stai chiedendo specificamente di Visual C++? Suppongo che altri compilatori potrebbero avere altri vincoli. –

+0

@ Robᵩ No, se mai stavo puntando a Linux, ICC e G ++. Ho appena afferrato la prima funzione di assemblatore che ho visto. – user997112

+1

Questo potrebbe essere leggermente OT, ma se un salto e un ritorno non comportano penalità troppo pesanti, considera di scrivere l'assemblatore in puro assemblatore (in un'unità separata di compilazione) per mantenere il tuo codice più portabile. Evitando l'inlining, a volte è possibile migliorare la latenza attraverso un utilizzo più efficiente della cache. Questo è più significativo sulle piattaforme embedded però. – psyill

risposta

1

Se l'asm in questione sta spingendo tutti i registri che utilizza in alto, poi li fa apparire in basso, penso che tu sia sicuro di non preoccuparti.

Nel tuo esempio, queste sono le istruzioni __asm push EAX e __asm pop EAX.

La vera risposta, suppongo, è che è necessario conoscere abbastanza ciò che fa il asm per essere sicuri di poterlo trattare come una scatola nera. :)

+0

Quindi, in pratica, assicurati che lo stato in cui inizi sia lo stato in cui hai finito? Cosa succede se vuoi restituire un calcolo dall'assemblatore, come faresti? – user997112

+0

Sì, assicurati di non rovinare lo stato. Il ritorno del valore dipenderà dal compilatore, penso. – Almo

3

mi piacerebbe rispondere sul subquestion:

vuol causare più problemi di risolvere, o è accettabile per specifiche attività di piccole dimensioni?

Lo fa sicuramente! Usando l'assemblatore in linea, si prende l'abilità dal compilatore per ottimizzare il codice. Non può eseguire la substizione di espressione parziale o qualsiasi altra ottimizzazione di fantasia. È davvero molto difficile produrre codice che sia migliore di quello che il compilatore emette con -O3. E come bonus, il codice diventa ancora migliore con la prossima versione del compilatore (presumendo che la prossima versione del compilatore non la romperà;)).

I compilatori di solito afferrano uno scopo più ampio di quello che il cervello umano potrebbe mai (o dovrebbe, per garantire la sanità mentale), essere in grado di allineare la funzione giusta al posto giusto, di eseguire una sostituzione di espressione parziale che renda il codice più efficiente. Cose che non faresti mai in ASM perché il tuo codice diventa illeggibile da morire.

Come riferimento aneddotico, vorrei scrivere su this post di Linus Torvalds, relativo all'implementazione git di SHA1, che supera la SHA1 ottimizzata a mano in libcrypt.

In effetti, penso che l'unico uso ragionevole dell'assemblatore inline oggigiorno stia chiamando le istruzioni del processore che non sono disponibili altrimenti (quello che hai citato è disponibile, su linux ad esempio come clock_gettime, almeno se sei solo dopo un contatore di tempo ad alta risoluzione) o se si devono fare cose in cui è necessario ingannare il compilatore (ad esempio durante l'implementazione di interfacce di funzioni esterne).


Sul frammento e su ciò che altri hanno detto. Soprattutto con tali funzioni otterrai una penalità sulle prestazioni. In linea asm, devi essere super-attento che i registri siano mantenuti nello stato che il compilatore presume che siano (push/pop, come sopra). Mentre se si scrive il codice normalmente, il compilatore può fare attenzione e mantenere esattamente quelle variabili per le quali ha senso nei registri e quelli che non si adattano allo stack.

Fidati del tuo compilatore. È intelligente La maggior parte delle volte. Investi il ​​tempo risparmiato evitando di utilizzare l'assemblatore in linea nel pensare a algoritmi intelligenti e veloci e ad apprendere i relativi switch del compilatore (ad esempio per abilitare le ottimizzazioni SSE, ecc.).

+0

Sicuramente si potrebbe obiettare, però, che un compilatore non può essere sorprendente in tutto. Quindi, per compensare l'ampia gamma di casi che può gestire, ci sono probabilmente molte aree in cui, per un piccolo compito specifico, un programmatore potrebbe scrivere meno istruzioni asm? – user997112

+0

@ user997112 Quali casi hai in mente? Pensando a tutto ciò che ha a che fare con i numeri, probabilmente non sarai in grado di tagliarlo. Nota anche che ho sostituito il riferimento, il mio originale era in realtà incluso in linea ASM. –

+0

Non ho nulla in mente, ma sarebbe certamente utile se fosse possibile scoprire se ci sono aree in cui i compilatori sono cattivi. – user997112

Problemi correlati