Memcpy très rapide pour le traitement d'image?
je fais du traitement d'image en C qui nécessite la copie de gros morceaux de données autour de la mémoire - la source et la destination ne se chevauchent jamais.
Quelle est la manière la plus rapide de le faire sur la plate-forme x86 en utilisant GCC (où SSE , SSE2 mais pas SSE3 sont disponibles)?
Je m'attends à ce que la solution soit en assemblage ou en utilisant GCC intrinsèques?
j'ai trouvé le suivant lien mais n'ont aucune idée si c'est la meilleure façon de le faire (l'auteur dit aussi qu'il a quelques bugs): http://coding.derkeiler.com/Archive/Assembler/comp.lang.asm.x86/2006-02/msg00123.html
EDIT: notez qu'une copie est nécessaire, Je ne peux pas me déplacer devant copier les données (je pourrais expliquer pourquoi mais je vous épargnerai l'explication :))
6 réponses
gracieuseté de William Chan et Google. 30-70% plus rapide que memcpy dans Microsoft Visual Studio 2005.
void X_aligned_memcpy_sse2(void* dest, const void* src, const unsigned long size)
{
__asm
{
mov esi, src; //src pointer
mov edi, dest; //dest pointer
mov ebx, size; //ebx is our counter
shr ebx, 7; //divide by 128 (8 * 128bit registers)
loop_copy:
prefetchnta 128[ESI]; //SSE2 prefetch
prefetchnta 160[ESI];
prefetchnta 192[ESI];
prefetchnta 224[ESI];
movdqa xmm0, 0[ESI]; //move data from src to registers
movdqa xmm1, 16[ESI];
movdqa xmm2, 32[ESI];
movdqa xmm3, 48[ESI];
movdqa xmm4, 64[ESI];
movdqa xmm5, 80[ESI];
movdqa xmm6, 96[ESI];
movdqa xmm7, 112[ESI];
movntdq 0[EDI], xmm0; //move data from registers to dest
movntdq 16[EDI], xmm1;
movntdq 32[EDI], xmm2;
movntdq 48[EDI], xmm3;
movntdq 64[EDI], xmm4;
movntdq 80[EDI], xmm5;
movntdq 96[EDI], xmm6;
movntdq 112[EDI], xmm7;
add esi, 128;
add edi, 128;
dec ebx;
jnz loop_copy; //loop please
loop_copy_end:
}
}
Vous pourriez être en mesure de l'optimiser en fonction de votre situation exacte, et les hypothèses que vous êtes capable de faire.
vous pouvez également consulter la source de memcpy (memcpy.asm) et de supprimer son traitement des cas spéciaux. Il peut être possible d'optimiser encore plus!
à n'importe quel niveau d'optimisation de -O1
ou plus, GCC utilisera des définitions intégrées pour les fonctions comme memcpy
- avec le paramètre -march
de droite ( -march=pentium4
pour l'ensemble des caractéristiques que vous mentionnez) il devrait générer le code en ligne assez optimal spécifique à l'architecture.
je le comparerais et verrais ce qui en sort.
le code SSE posté par hapalibashi est la voie à suivre.
si vous avez besoin d'encore plus de performances et n'hésitez pas à vous éloigner de la route longue et sinueuse de l'écriture d'un pilote de périphérique: toutes les plates-formes importantes ont aujourd'hui un DMA-controller qui est capable de faire un travail de copie plus rapidement et en parallèle avec le code CPU pourrait faire.
qui implique d'écrire un pilote. Aucun grand OS que je suis au courant expose cette fonctionnalité du côté de l'utilisateur en raison de la les risques de sécurité.
cependant, il peut en valoir la peine (si vous avez besoin de la performance) puisqu'aucun code sur terre ne pourrait dépasser un morceau de matériel qui est conçu pour faire un tel travail.
cette question a maintenant quatre ans et je suis un peu surpris que personne n'ait encore mentionné la bande passante mémoire. CPU-Z rapporte que ma machine a PC3-10700 RAM. Que la mémoire vive a une bande passante de pointe (vitesse de transfert, débit, etc.) de 10700mytes/ sec. Le CPU de ma machine est un CPU i5-2430M, avec une fréquence de pointe de 3 GHz.
théoriquement, avec un CPU infiniment rapide et ma RAM, memcpy pourrait aller à 5300 MBytes / sec , soit la moitié de 10700 parce que memcpy doit lire à partir de et ensuite écrire à RAM. (edit: Comme v. oddou signalé, c'est une simple approximation).
d'un autre côté, imaginez que nous avions un RAM infiniment rapide et un CPU réaliste, que pourrions-nous atteindre? Utilisons mon processeur 3 GHz comme exemple. S'il pouvait lire 32 bits et écrire 32 bits à chaque cycle, alors il pourrait transférer 3e9 * 4 = 12000 MBytes/sec . Cela semble accessible, pour un PROCESSEUR récent. Déjà, nous pouvons voir que le code qui tourne sur le CPU n'est pas vraiment le goulot d'étranglement. C'est l'une des raisons pour lesquelles les machines modernes disposent de caches de données.
nous pouvons mesurer ce que le CPU peut vraiment faire en comparant memcpy quand nous savons que les données sont cachées. Faire cette précision est délicat. J'ai créé une application simple qui a écrit des nombres aléatoires dans un tableau, je les ai mémorisés dans un autre tableau, puis j'ai vérifié les données copiées. Je traversai le code dans le débogueur assurez-vous que l'habile compilateur avait pas supprimé la copie. La modification de la taille du tableau modifie les performances du cache - les petits tableaux s'adaptent au cache, les grands moins. J'ai eu les résultats suivants:
- 40 Ko tableaux: 16000 Mo/sec
- matrices de 400 Kbytes: 11000 MBytes /sec
- 4000 Ko tableaux: 3100 Mo/sec
évidemment, mon CPU peut lire et écrire plus de 32 bits par cycle, puisque 16000 est plus de le 12000 que j'ai calculé théoriquement ci-dessus. Cela signifie que le CPU est encore moins un goulot d'étranglement que je le pensais déjà. J'ai utilisé Visual Studio 2005, et en entrant dans l'implémentation standard de memcpy, je peux voir qu'il utilise l'instruction movqda sur ma machine. Je suppose que cela peut lire et écrire 64 bits par cycle.
le code agréable hapalibashi affiché atteint 4200 Moytes/sec sur ma machine - environ 40% plus rapide que L'implémentation VS 2005. Je suppose que c'est plus rapide car il utilise l'instruction prefetch pour améliorer les performances du cache.
en résumé, le code qui tourne sur le CPU n'est pas le goulot d'étranglement et le réglage de ce code ne fera que de petites améliorations.
si spécifique aux processeurs Intel, vous pourriez bénéficier de IPP . Si vous savez qu'il fonctionnera avec un GPU Nvidia peut - être que vous pourriez utiliser CUDA - dans les deux cas, il peut être préférable d'avoir l'air plus large que d'optimiser memcpy () - ils fournissent des opportunités pour améliorer votre algorithme à un niveau plus élevé. Ils dépendent tous deux d'un matériel spécifique.
si vous êtes sur Windows, utilisez le DirectX APIs, qui a spécifique GPU - routines optimisées pour le traitement des graphiques (à quelle vitesse pourrait-il être? Votre CPU n'est pas chargé. Faire autre chose pendant que le GPU le munche).
si vous voulez être agnostique, essayez OpenGL .
ne pas jouer avec assembleur, parce qu'il est trop probable que vous échouerez misérablement à dépasser 10 ans+ ingénieurs logiciels compétents en bibliothéconomie.