8
votes

La fonction "personnalisée intrinsèque" pour X64 au lieu d'assemblage en ligne possible?

J'exprime actuellement la création de fonctions très optimisées et réutilisables pour une bibliothèque de milles. Par exemple, j'écris la fonction "est la puissance de 2" de la manière suivante: xxx pré>

Il s'agit d'une implémentation portable et basse-maintenance en tant que modèle en ligne C ++. Ce code est compilé par VC ++ 2008 au code suivant avec les succursales: p> xxx pré>

J'ai trouvé aussi la mise en œuvre à partir d'ici: " The Bit Twiddler ", qui serait codé en montage pour X64 comme suit: P>

is_power_of_two_fast PROC
    test rcx, rcx
    je  SHORT NotAPowerOfTwo
    lea rax, [rcx-1]
    and rax, rcx
    neg rax
    sbb rax, rax
    inc rax
    ret
NotAPowerOfTwo:
    xor rax, rax
    ret
is_power_of_two_fast ENDP


3 commentaires

GCC et ICC permettent toujours l'assemblage en ligne


Évitez la branche en utilisant et au lieu de &&.


@drhirsch: Merci, je garde ça à l'esprit. @Hans Passant: J'ai déjà essayé, mais cela conduit à un code plus lent (trop d'instructions).


4 Réponses :


0
votes

La seule voie à suivre consiste à reculer un peu et à commencer à regarder l'image plus grande. Soit arrêter la mise en œuvre d'une API micro-optimisée ou de progresser sur la fabrication d'appels d'API plus importants tous optimisés dans MASM64, Yasm, NasM, etc.

Si vous utilisez l'un des assembleurs les plus puissants, vous pouvez transformer les petites fonctions en macros, modifiez ainsi la fonction d'assembleur d'en-tête C / C ++ basée sur l'en-tête C / C ++ en un assembleur Inclure le fichier.


0 commentaires

3
votes

Non, vous ne pouvez implémenter aucune intrigue personnalisée, elles sont toutes intégrées au compilateur. Ce n'est pas seulement les instructions intégrées, mais le compilateur connaît également la sémantique de l'intrinsèque et adapte le code pour un code environnant différent.

Une des raisons de l'assemblage en ligne étant supprimée du X86-64 est que l'insertion de l'assemblage au milieu d'une fonction perturbe l'optimiseur et donne souvent moins de code optimisé sur le code de l'assembleur. Il peut facilement y avoir une perte nette là-bas!

Le seul usage réel de l'intrinsique est destiné aux instructions spéciales "intéressantes" que le compilateur ne peut pas générer de constructions C ou C ++, telles que BSF ou BSR. La plupart de tout le reste fonctionnera mieux en utilisant des fonctions inline, comme votre modèle ci-dessus.

Si vous avez besoin de faire quelque chose de spécial, le compilateur ne comprend pas, la seule option réelle consiste à écrire la fonction entière sous forme de module d'assembleur séparé. Si l'appel au-dessus de cette fonction est trop coûteux, l'optimisation n'allait probablement pas que beaucoup en premier lieu.

Faites confiance à votre compilateur (TM)!


0 commentaires

1
votes

VC10 X64 intrinsics ne serait pas d'une grande aide dans ce cas simple. La ramification dynamique que vous avez est due à l'opérateur && qui est un opérateur de tôt imprimé. Dans de nombreux cas (votre cas est un exemple parfait) Il est préférable d'éviter la ramification en calculant le résultat pour toutes les branches, puis appliquez un masque pour sélectionner le bon. Un code CPP avec masquage ressemblerait à: xxx

dans le code ci-dessus, je ne vérifie pas si le nombre est négatif ou non pour les types signés. Encore une fois, un masque simple fera le tour en effectuant un décalage arithmétique à droite (NUMBIT-1) fois pour obtenir une valeur de (~ 0) pour des nombres négatifs et 0 pour ceux positifs


1 commentaires

Ce que vous suggérez, c'est malheureusement pas très différent de la fonction initiale C ++. Une compilation avec la sortie d'assemblage révèle que VC ++ 2008 utilise l'instruction "Test" lors de la compilation de votre code et les branches sont toujours là.



2
votes

Même VC 2005 est capable de produire du code avec des instructions SBB.

pour Code C P>

00401000  lea         eax,[ecx-1] 
00401003  and         eax,ecx 
00401005  cmp         eax,1 
00401008  sbb         eax,eax 
0040100A  neg         eax  
0040100C  cmp         ecx,1 
0040100F  sbb         ecx,ecx 
00401011  add         ecx,1 
00401014  and         eax,ecx 
00401016  ret          


1 commentaires

Mes excuses si c'est une question stupide, mais: non __ DeclSpec (NOINLINE) signifie qu'il ne peut pas être inlincé par le compilateur, nécessitant la surcharge d'un appel de la fonction (quel op d'envisage pour éviter la version CDB à courir plus vite)?