9
votes

Bits d'ordre élevé - prenez-les et faites un uint64_t dans un uint8_t

Disons que vous avez un uint64_t et ne vous souciez que du bit de commande élevé pour chaque octet de votre uint64_t. Comme:

uint32_t: 0000 ... 1000 0000 1000 0000 1000 0000 1000 0000 0000 0000 0000 ---> 0000 1111

Y a-t-il un moyen plus rapide que: xxx

aka changeant x , masquage et ajout du bit correct pour chaque octet? Cela compilera à beaucoup d'assemblée et je cherche un moyen plus rapide ... La machine que j'utilise uniquement a uniquement des instructions SSE2 et je n'ai pas réussi à trouver des ops de SIMD utiles.

Merci pour l'aide.


7 commentaires

Vous pouvez réinterpréter les octets simples, en boucle et masquer les bits simples. Dunno Si cela est plus rapide, mais peut-être que le compilateur peut mieux l'optimiser.


Peut-être que vous pouvez d'abord masquer avec 0x8080808080808080


Avez-vous besoin du résultat, c'est-à-dire une séquence de 8 bits en tant que nombre? Ou ne vérifierais-t-il que si les bits HO sont 1 ou non, suffisent pour vous?


Oui, pmovmskb fait exactement ce que vous voulez. IIRC Il y aura une instruction entière dans AVX2 que vous pouvez utiliser pour faire la même chose (les bits de Gather, oublié le mnémonique).


Harold, vous devriez vraiment faire une réponse, pas un commentaire. Il est absolument correct, sur Intel L'instruction SSE est ce que vous voulez. Notez qu'il existe déjà un codage AVX, bien qu'il ne fonctionne que sur les 16 octets du bas du registre YMM.


@Andyross J'étais en train de l'écrire, a pris un certain temps parce que je vraiment voulait mettre cette nouvelle instruction là-bas :)


La machine de OP n'a pas de quoi que ce soit après SSE2, alors que la nouvelle façon est agréable, elle n'est probablement pas pertinente.


6 Réponses :


4
votes

Vous n'avez pas besoin de tout le logique distinct, vous pouvez le simplifier à:

uint8_t r = 0;

x &= 0x8080808080808080;

x >>= 7; r |= x;
x >>= 7; r |= x;
x >>= 7; r |= x;
x >>= 7; r |= x;
x >>= 7; r |= x;
x >>= 7; r |= x;
x >>= 7; r |= x;
x >>= 7; r |= x;
return r;


5 commentaires

Et la question de million de dollars est la suivante: fait gcc -ssse générer pmovmskb pour ce code? :)


Vous voudrez probablement qualifier cette constante comme ull pour que le compilateur n'essaie pas de jouer des tours avec des valeurs signées.


@MarkB: Ce n'est pas nécessaire en C ++ 11.


Je suis à peu près sûr que l'ULL n'est jamais nécessaire.


Il n'est pas nécessaire en C99 non plus - car x n'est pas signé, même si la constante est signée, elle sera favorisée non signée (ceci est vrai même si le type de constante est plus large que uint64_t < / code>).



11
votes

Comme je l'ai mentionné dans un commentaire, PMOVMSKB code> fait ce que vous voulez. Voici comment vous pouvez l'utiliser:

MMX + SSE1: P>

mov rax, 0x8080808080808080
pext output, input, rax ; input must be r


5 commentaires

+1 Si vous ajoutez l'ASM en ligne correct (avec des contraintes appropriées) pour générer un code optimal à l'aide de cette méthode.


@R .. Je voudrais, mais je n'ai jamais fait ça auparavant. J'essaie de ne pas toucher GCC avec un pôle de 10 pieds. J'ai examiné ces contraintes et, bien, peut-être que ce code apparaîtra dans .. peut-être


Ok +1 quand même. Je vais l'ajouter si j'ai le temps de regarder comment le faire.


N'y a-t-il pas un simple intrinsèque pour cette ASM?


@Rubenvb tu me dis. Je n'ai jamais compris comment MOVQ d'un registre avec intrinsique.



5
votes

Et voici comment le faire en utilisant SSE Intrinsics: xxx

fonctionne bien avec: xxx


0 commentaires

0
votes

Cela semble fonctionner: xxx


1 commentaires

Pas si vous avez le premier jeu défini et donc besoin d'une réponse> = 128.



2
votes

Tout d'abord, vous n'avez pas vraiment besoin de tant d'opérations. Vous pouvez agir sur plus d'un bit à la fois:

ull t = ((x & 0x8080808080808080) >> 7) % 0xFFFC;
return (t | (t>>7)) & 0xFF;


0 commentaires

10
votes
return ((x & 0x8080808080808080) * 0x2040810204081) >> 56;
works. The & selects the bits you want to keep. The multiplications all the bits into the most significant byte, and the shift moves them to the least significant byte. Since multiplication is fast on most modern CPUs this shouldn't be much slower than using assembly.

2 commentaires

Cela pourrait réellement être plus rapide que PMOVMSK , une instruction assez lente.


@Drhirsch Latence de cycle 2 (3 sur AMD K10) et un débit de 1 sur une base2, pas si mal du tout .. même la multiplication ici est pire.