6
votes

Utilisation des instructions d'assemblage BTS avec le compilateur GCC

Je souhaite utiliser les instructions de montage BTS et BT X86 pour accélérer les opérations de bits sur mon code C ++ sur le Mac. Sous Windows, les intrinsèques _bittestandSet et _bittest fonctionnent bien et fournissent des gains de performance significatifs. Sur le Mac, le compilateur GCC ne semble pas supporter ceux-ci, alors j'essaie de le faire directement dans l'assembleur à la place.

Voici mon code C ++ (note que "bit" peut être> = 32): xxx

Le code d'assembleur suivant fonctionne, mais n'est pas optimal, car le compilateur ne peut pas optimiser l'allocation de registres: xxx

question : Comment puis-je obtenir le compilateur pour optimiser complètement autour de l'instruction BTS? Et comment puis-je remplacer Testbit par une instruction BT?


2 commentaires

Pas une réponse directe, juste un lien vers GCC "rel =" Nofollow NOREFERRER "> cims.nyu.edu/cgi-systems/... Documentation ASM étendue .


bt * avec un opérande de mémoire est lent, à cause de la sémantique Crazy Cisc. En fait, il est plus rapide de laisser le compilateur émettre la séquence d'instructions de décalage / ou de test) que vous obtiendriez avec ce code. (Au moins, une version de BugFixed et 64 bits de ceci).


3 Réponses :


-1
votes

Une autre réponse légèrement indirecte, GCC expose Un certain nombre d'opérations atomiques commençant par la version 4.1.


0 commentaires

5
votes
inline void SetBit(*array, bit) {
    asm("bts %1,%0" : "+m" (*array) : "r" (bit));
}

5 commentaires

Parfait merci. Cela m'a aidé à comprendre ma deuxième question: Inline Bool Testbit (Array Constworwords [], const int bit) {drapeau BOOL; ASM ("BT% 2,% 1; SETB% 0": "= q" (drapeau): "m" (* tableau), "r" (bit)); drapeau de retour; }


@Efement: Pouvez-vous ajouter des explications à votre réponse.


Si bit peut être en dehors de la plage 0..31, votre opérande de mémoire doit être l'ensemble de la matrice, pas seulement le premier élément de celui-ci. (N'oubliez pas la sémantique de string bit-string Crazy-Cisc de BTS avec Un opérande de mémoire.) De plus, vous devez laisser la source opérande être "ri" , car il fonctionne avec des opérandes immédiats (et est beaucoup moins lent de cette façon). Mais vous ne devriez vraiment pas l'utiliser du tout sur le X86 moderne. BTS avec un opérande de mémoire est lent (voir ma réponse).


Cette instruction gère le drapeau C, mais l'assembly ne précise pas cela.


@MaximeGorushkin: Un clobber "CC" est implicite dans GNU C Inline ASM pour X86 et X86-64. Ce n'est pas un bug, bien que certaines personnes considèrent que c'est un bon style de la spécifier manuellement de toute façon.



7
votes

BTS (et l'autre bt * insns) avec une destination de mémoire sont lents. (> 10 UOPS sur Intel) . Vous aurez probablement un code plus rapide de faire l'adresse Math pour trouver le bon octet et le charger dans un registre. Ensuite, vous pouvez faire le bt / BTS avec une destination d'enregistrement et stockez le résultat.

ou peut-être changer un 1 à la bonne position et utilisez ou avec une destination de mémoire pour setbit ou et avec une source de mémoire pour testbit . Bien sûr, si vous évitez l'ASM en ligne, le compilateur peut en ligne testbit et utiliser test au lieu de et , qui est utile sur certains processeurs (puisque Il peut macro-fusible dans une branche de test sur plus de CPU que et ).

C'est en fait ce que GCC 5.2 génère de votre source C (Memory-Dest ou ou test ). On dirait optimal pour moi (moins d'UOPS qu'un Memory-Dest BT ). En fait, notez que votre code est cassé car il suppose non signé long est de 32 bits, pas char_bit * taille de (non signé_long) . Utilisation de uint32_t ou Char serait un plan bien meilleur. Notez l'extension de signe de EAX dans RAX avec l'instruction CQDE , en raison du C mal écrit qui utilise 1 au lieu de 1UL .

Notez également que Inline ASM ne peut pas renvoyer les indicateurs comme résultat (sauf avec un 8 commentaires

En fait, à partir de v6, gcc peut drapeaux de retour.


@Davidwohlferd: merci! C'est intéressant qu'ils ont finalement inclus cela.


@Petercordes Avez-vous déjà vu un compilateur générer BTS ? Je pouvais effectivement obtenir au moins clang et icc pour générer bt pour "bit de test" type fonctionne mais n'a eu aucune chance avec BTS même s'il semblerait très utile pour la fonction de type" jeu de bit "par rapport à Les alternatives de l'utilisation de shl ou shlx et ou et un couple ops supplémentaire pour charger la constante 1 etc.


@Beeonrope: j'oublie. Ce que vous décrivez des sons familiers: en utilisant BT pour tester les bits, mais omis d'utiliser BTS pour le bit de jeu. Je suis sûr que c'est une victoire sur Intel CPus au moins, pour des trucs comme foo | = 1 << n , où n n'est pas une constante de compilation et foo est déjà dans un registre. Oui, juste testé, et pas de chance: godbolt.org/g/9s6i9d


Ouais, c'était exactement l'idome que j'ai testé dans le lien Godbolt. Je a fait trouver un moyen de générer BTS sur icc uniquement, dans le cas spécifique d'un index de bit de compilation . Ni GCC ni clang Utiliser BTS là et ICC ne l'utilise pas si la position est variable (où Il est sans doute plus utile, car le code non- BTS pour les changements variables est généralement encore plus lent que le boîtier constant, notamment pré- shlx ).


@Beeonrope: hein, cas d'utilisation intéressante. Je suppose que ou r64, IMM32 ne fonctionnerait pas là-bas, car la constante ne peut pas être représentée comme un signe étendu imm32. D'accord que l'utiliser pour les changements variables est une plus grande victoire.


@Beeonrope: Re: Manque de réponses récentes. Pas exactement, mais j'en ai marre de voir les mêmes questions débutantes tout le temps que j'ai pris une pause pendant un moment pour rattraper la télévision / les films / livres que je voulais regarder / lire.


@Petercordes, duh, bon point. J'avais seulement testé avec une telle constante en raison de la copie-colle d'un autre test: mais je n'avais pas explicitement destiné à utiliser 64 bits. Bien sûr, pour les constantes de 32 bits, ou est généralement meilleure en raison de plusieurs unités d'exécution et de la vitesse moyenne plus rapide sur de nombreuses architectures ( BTS n'a pas été 1 cycle pour toujours, AFAIK). En effet, tout le monde utilise ou pour des constantes 32 bits ou moins (vous pouvez toujours faire un cas mineur pour BTS car il est 5 octets vs ou -with-32 bits-constants de 7 octets).