Question

Je copier des éléments de réseau à une autre en C ++. J'ai trouvé l'instruction rep movs en x86 qui semble copier un tableau d'ESI à un tableau à l'EDI de la taille ECX. Cependant, ni le for ni les boucles de while I Tried compilé à une instruction de rep movs dans VS 2008 (sur un processeur Intel Xeon 64 bits). Comment puis-je écrire un code qui se compilé à cette instruction?

Était-ce utile?

La solution

Si vous avez besoin exactement cette instruction - utilisation intégrée en assembleur et écrire cette instruction manuellement. Vous ne pouvez pas compter sur le compilateur pour produire un code machine spécifique - même si elle émet dans une seule compilation, il peut décider pour émettre un autre équivalent lors de la compilation suivante.

Autres conseils

Honnêtement, vous ne devriez pas. REP est une sorte d'une durée d'efficacité obsolète dans le jeu d'instructions, et en fait assez lent car il doit appeler un sous-programme microcode dans la CPU, qui a une latence recherche ROM et est nonpipelined ainsi.

Dans presque chaque implémentation, vous trouverez que le compilateur memcpy() intrinsèque à la fois est plus facile à utiliser et fonctionne plus rapidement.

Dans MSVC il y a __movsxxx et __stosxxx intrinsics qui va générer une instruction préfixée de REP.

il y a aussi un « hack » à la force intrinsèque memset aka REP STOS sous VC9 +, comme les pas intrinsèques sort plus, en raison de la SSE2 ramification dans le crt. ce qui est mieux que __stosxxx en raison du fait que le compilateur peut optimiser pour les constantes et l'ordre correctement.

#define memset(mem,fill,size) memset((DWORD*)mem,((fill) << 24|(fill) << 16|(fill) << 8|(fill)),size)
__forceinline void memset(DWORD* pStart, unsigned long dwFill, size_t nSize)
{
    //credits to Nepharius for finding this
    DWORD* pLast = pStart + (nSize >> 2);
    while(pStart < pLast)
        *pStart++ = dwFill;

    if((nSize &= 3) == 0)
        return;

    if(nSize == 3)
    {
        (((WORD*)pStart))[0]   = WORD(dwFill);
        (((BYTE*)pStart))[2]   = BYTE(dwFill);
    }
    else if(nSize == 2)
        (((WORD*)pStart))[0]   = WORD(dwFill);
    else
        (((BYTE*)pStart))[0]   = BYTE(dwFill);
}

Bien sûr REP n'est pas toujours la meilleure chose à utiliser, imo votre chemin mieux à l'aide memcpy, il va se ramifier soit SSE2 ou REPS MOV en fonction de votre système (sous msvc), à moins que vous sentez comme écrire assemblage sur mesure pour les zones 'chaudes' ...

REP et les amis était bien une fois, lorsque le CPU x86 était un seul pipeline CISC-processeur industriel.

Mais cela a changé. Aujourd'hui, quand les rencontres de processeur tout instruction, la première, il ne se traduit dans un format plus facile (VLIW comme les micro-ops) et les horaires il pour l'exécution future (ce qui fait partie de hors-commande -Exécution, une partie de l'ordonnancement entre les différents noyaux de processeur logiques, il peut être utilisé pour la simplification des séquences d'écriture après écriture-en-écriture unique, et.c.). Ce mécanisme fonctionne bien pour les instructions qui se traduit par quelques VLIW comme opcodes, mais pas le code machine qui se traduit par des boucles. Code machine traduit la boucle sera probablement provoquer le pipeline d'exécution de décrochage.

Au lieu de dépenser des centaines de milliers de transistors dans la construction-circuit de CPU pour traiter les parties looping des micro-ops dans le pipeline d'exécution, ils traitent tout dans une sorte de legs en mode merdique qui cale stutterly le pipeline, et demandez programmeurs modernes d'écrire vos propres boucles damn!

Par conséquent, il est rarement utilisé lorsque les machines écrire du code. Si vous rencontrez des REP dans un exécutable binaire, est probablement un assemblage Muppet humain qui ne savait pas mieux, ou un pirate qui a vraiment besoin de quelques octets, il a sauvé l'utiliser au lieu d'une boucle réelle, qui l'a écrit.

(Cependant. Prenez tout ce que je viens d'écrire avec un grain de sel. Peut-être que ce n'est pas plus vrai. Je ne suis pas 100% à jour avec les processeurs x86 internes de plus, je suis entré dans d'autres passe-temps ..)

J'utilise le représentant * préfixe des variantes avec cmps *, movs *, scas * et stos * instruction des variantes pour générer le code en ligne qui minimise la taille du code, évite les appels inutiles / sauts et maintient ainsi sur le travail effectué par les caches. L'alternative consiste à configurer les paramètres et appeler un memset ou memcpy un autre endroit qui peut être globalement plus rapide si je veux copier cent octets ou plus mais si elle est juste une question de 10-20 octets en représentant est plus rapide (ou tout au moins était la dernière fois que je l'ai mesuré).

Depuis mon compilateur me permet de préciser et de l'utilisation des fonctions assembleur en ligne et comprend leur utilisation / modification registre dans les activités d'optimisation, il est possible de les utiliser lorsque les circonstances le permettent.

Sur une note historique - ne pas avoir de comprendre les stratégies du fabricant - il y avait un moment où les « movs rep * » (etc.) des instructions ont été très lents. Je pense qu'il était à l'époque du Pentium / Pentium MMX. Un de mes collègues (qui avait plus de perspicacité que moi) dit que les fabricants avaient diminué la surface de la puce (<=> moins de transistors / plus microcode) affectés à la manipulation de représentant et l'a utilisé pour faire d'autres, des instructions plus utilisées plus rapidement.

Dans les quinze ans ou plus depuis représentant est devenu relativement parlant encore plus rapide qui suggère plus de transistors / moins microcode.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top