Domanda

Esistono alternative più veloci a memcpy () in C ++?

È stato utile?

Soluzione

Improbabile. Il tuo compilatore / libreria standard avrà probabilmente un'implementazione molto efficiente e su misura di memcpy. E memcpy è fondamentalmente l'api più basso che c'è per copiare una parte della memoria in un'altra.

Se desideri ulteriori accelerazioni, trova un modo per non aver bisogno di alcuna copia di memoria.

Altri suggerimenti

Primo, un consiglio. Supponi che le persone che hanno scritto la tua libreria standard non siano stupide. Se ci fosse un modo più veloce per implementare un memcpy generale, l'avrebbero fatto.

In secondo luogo, sì, ci sono alternative migliori.

  • In C ++, utilizzare la funzione std :: copy . Fa la stessa cosa, ma è 1) più sicuro e 2) potenzialmente più veloce in alcuni casi. È un modello, il che significa che può essere specializzato per tipi specifici, rendendolo potenzialmente più veloce del memcpy C generale.
  • Oppure, puoi usare la tua conoscenza superiore della tua situazione specifica. Gli implementatori di memcpy hanno dovuto scriverlo, quindi ha funzionato bene in ogni caso. Se disponi di informazioni specifiche sulla situazione in cui ne hai bisogno, potresti essere in grado di scrivere una versione più veloce. Ad esempio, quanta memoria devi copiare? Come è allineato? Ciò potrebbe consentire di scrivere un memcpy più efficiente per questo caso specifico. Ma non sarà altrettanto buono nella maggior parte degli altri casi (se funzionerà)

L'esperta di ottimizzazione Agner Fog ha pubblicato funzioni di memoria ottimizzate: http://agner.org/optimize/#asmlib . È sotto GPL però.

Qualche tempo fa Agner ha affermato che queste funzioni dovrebbero sostituire i builtin di GCC perché sono molto più veloci. Non so se sia stato fatto da allora.

Questa risposta per una domanda molto simile (su memset () ) si applica anche qui.

In pratica dice che i compilatori generano un codice molto ottimale per memcpy () / memset () - e un codice diverso a seconda della natura degli oggetti (dimensione, allineamento , ecc.)

E ricorda, solo memcpy () POD in C ++.

Per trovare o scrivere una routine di copia veloce della memoria, dovremmo capire come funzionano i processori.

Processori da Intel Pentium Pro & # 8220; Esecuzione fuori servizio & # 8221 ;. Possono eseguire molte istruzioni in parallelo se le istruzioni non hanno dipendenze. Ma questo è solo il caso in cui le istruzioni funzionano solo con i registri. Se funzionano con la memoria, vengono utilizzate unità CPU aggiuntive, chiamate & # 8220; unità di carico & # 8221; (per leggere i dati dalla memoria) e & # 8220; unità di memoria & # 8221; (per scrivere i dati in memoria). La maggior parte delle CPU ha due unità di carico e un'unità di memorizzazione, ovvero possono eseguire in parallelo due istruzioni che leggono dalla memoria e un'istruzione che scrive nella memoria (di nuovo, se non si influenzano a vicenda). La dimensione di queste unità è generalmente uguale alla dimensione massima del registro & # 8211; se la CPU ha registri XMM (SSE) & # 8211; è di 16 byte, se ha registri YMM (AVX) & # 8211; è di 32 byte e così via. Tutte le istruzioni che leggono o scrivono la memoria vengono tradotte in micro-operazioni (micro-operazioni) che vanno al pool comune di micro-operazioni e attendono lì che il carico e le unità di memoria siano in grado di servirle. Un singolo carico o unità di archiviazione può servire solo una micro-operazione alla volta, indipendentemente dalle dimensioni dei dati che deve caricare o archiviare, sia esso 1 byte o 32 byte.

Quindi, la copia di memoria più veloce verrebbe spostata da e verso i registri con dimensioni massime. Per i processori abilitati per AVX, il modo più veloce per copiare la memoria sarebbe quello di ripetere la seguente sequenza, loop-unrolled:

vmovdqa     ymm0,ymmword ptr [rcx]
vmovdqa     ymm1,ymmword ptr [rcx+20h]
vmovdqa     ymmword ptr [rdx],ymm0
vmovdqa     ymmword ptr [rdx+20h],ymm1

Il codice Google pubblicato in precedenza da hplbsh non è molto buono, perché usano tutti i registri 8 xmm per conservare i dati prima che inizino a riscriverli, mentre non è necessario & # 8211; poiché abbiamo solo due unità di carico e una unità di deposito. Quindi solo due registri danno i migliori risultati. L'uso di così tanti registri non migliora in alcun modo le prestazioni.

Una routine di copia di memoria può anche usare alcuni "avanzati" tecniche come & # 8220; prefetch & # 8221; per indicare al processore di caricare in anticipo la memoria nella cache e & # 8220; scritture non temporali & # 8221; (se stai copiando blocchi di memoria molto grandi e non hai bisogno di leggere immediatamente i dati dal buffer di output), allineati vs scritture non allineate, ecc.

I processori moderni, rilasciati dal 2013, se hanno il bit ERMS nel CPUID, hanno il cosiddetto & # 8220; mov rep di rep migliorato & # 8221 ;, quindi per la copia di memoria di grandi dimensioni, il rep # movsb & # 8221 ; può essere utilizzato & # 8211; la copia sarà molto veloce, anche più veloce rispetto ai registri ymm e funzionerà correttamente con la cache. Tuttavia, i costi di avvio di questa istruzione sono molto elevati & # 8211; circa 35 cicli, quindi paga solo su blocchi di memoria di grandi dimensioni.

Spero che ora dovrebbe essere più facile per te scegliere o scrivere la migliore routine di copia di memoria necessaria per il tuo caso.

Puoi persino conservare il memcpy / memmove standard, ma ottenere il tuo speciale grandeememppy () per le tue esigenze.

A seconda di ciò che stai cercando di fare ... se è un memcpy abbastanza grande, e stai scrivendo solo sulla copia scarsamente, una mmap con MMAP_PRIVATE per creare una mappatura copia su scrittura potrebbe essere più veloce .

A seconda della piattaforma in uso potrebbero esserci casi d'uso specifici, ad esempio se si sa che l'origine e la destinazione sono allineate a una riga della cache e la dimensione è un multiplo intero della dimensione della riga della cache. In generale la maggior parte dei compilatori produrrà comunque un codice abbastanza ottimale per memcpy.

Non sono sicuro che l'uso della memcpy predefinita sia sempre l'opzione migliore. La maggior parte delle implementazioni memcpy che ho visto tendono a provare ad allineare i dati all'inizio, quindi a fare copie allineate. Se i dati sono già allineati o sono piuttosto piccoli, significa perdere tempo.

A volte è utile avere una copia di parole specializzata, una copia di mezza parola, una copia di byte memcpy, purché non abbia un effetto troppo negativo sulle cache.

Inoltre, potresti desiderare un controllo più preciso sull'algoritmo di allocazione effettivo. Nel settore dei giochi è eccezionalmente comune per le persone scrivere le proprie routine di allocazione della memoria, indipendentemente da quanto sforzo sia stato speso dagli sviluppatori di toolchain in primo luogo per svilupparlo. I giochi che ho visto tendono quasi sempre a usare Malloc di Doug Lea .

In generale, però, sprecheresti tempo a cercare di ottimizzare memcpy poiché senza dubbio ci saranno molti più semplici frammenti di codice nella tua applicazione per accelerare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top