8 bits représentant le nombre 7 ressemblent à ceci:
00000111
Trois bits sont définis.
Quels sont les algorithmes pour déterminer le nombre de bits définis dans un entier 32 bits?
algorithm
binary
bit-manipulation
hammingweight
iec10967
Matt Howells
la source
la source
Réponses:
Ceci est connu sous le nom de « poids Hamming », «popcount» ou «addition latérale».
Le «meilleur» algorithme dépend vraiment du processeur sur lequel vous vous trouvez et de votre modèle d'utilisation.
Certains processeurs ont une seule instruction intégrée pour le faire et d'autres ont des instructions parallèles qui agissent sur les vecteurs de bits. Les instructions parallèles (comme les x86
popcnt
, sur les processeurs où il est pris en charge) seront presque certainement les plus rapides. Certaines autres architectures peuvent avoir une instruction lente implémentée avec une boucle microcodée qui teste un bit par cycle ( citation nécessaire ).Une méthode de recherche de table pré-remplie peut être très rapide si votre CPU dispose d'un grand cache et / ou si vous exécutez beaucoup de ces instructions dans une boucle serrée. Cependant, il peut souffrir à cause du coût d'un «échec de cache», où le CPU doit récupérer une partie de la table de la mémoire principale. (Recherchez chaque octet séparément pour garder la table petite.)
Si vous savez que vos octets seront principalement des 0 ou des 1, il existe des algorithmes très efficaces pour ces scénarios.
Je crois qu'un très bon algorithme à usage général est le suivant, connu sous le nom d'algorithme SWAR «parallèle» ou «à précision variable». Je l'ai exprimé dans un pseudo-langage de type C, vous devrez peut-être l'ajuster pour qu'il fonctionne pour un langage particulier (par exemple en utilisant uint32_t pour C ++ et >>> en Java):
Pour JavaScript: contraindre à un entier avec
|0
pour des performances: changez la première ligne eni = (i|0) - ((i >> 1) & 0x55555555);
Cela a le meilleur comportement dans le pire des cas de tous les algorithmes discutés, donc traitera efficacement tout modèle d'utilisation ou les valeurs que vous lui lancerez.
Comment fonctionne ce bithack SWAR:
La première étape est une version optimisée du masquage pour isoler les bits pairs / impairs, les décaler pour les aligner et les ajouter. Cela fait effectivement 16 ajouts distincts dans des accumulateurs 2 bits ( SWAR = SIMD dans un registre ). Comme
(i & 0x55555555) + ((i>>1) & 0x55555555)
.L'étape suivante prend les huit paires paires / impaires de ces 16x accumulateurs 2 bits et les ajoute à nouveau, produisant des sommes 8x 4 bits. L'
i - ...
optimisation n'est pas possible cette fois-ci, elle masque donc juste avant / après le décalage. L'utilisation de la même0x33...
constante les deux fois plutôt0xccc...
qu'avant le décalage est une bonne chose lors de la compilation pour les ISA qui doivent construire des constantes 32 bits dans des registres séparément.La dernière étape de changement et d'ajout de
(i + (i >> 4)) & 0x0F0F0F0F
s'élargit à 4 accumulateurs 8 bits. Il masque après l' ajout au lieu d'avant, car la valeur maximale dans tout accumulateur à 4 bits est4
, si les 4 bits des bits d'entrée correspondants ont été définis. 4 + 4 = 8 qui tient toujours sur 4 bits, donc le transfert entre les éléments de quartet est impossible dansi + (i >> 4)
.Jusqu'à présent, il s'agit simplement d'une carte SIMD assez normale utilisant des techniques SWAR avec quelques optimisations intelligentes. Continuer avec le même modèle pour 2 étapes supplémentaires peut s'étendre à 2 x 16 bits puis 1 x 32 bits. Mais il existe un moyen plus efficace sur les machines à multiplication matérielle rapide:
Une fois que nous avons assez "d'éléments", une multiplication avec une constante magique peut additionner tous les éléments dans l'élément supérieur . Dans ce cas, les éléments d'octet. La multiplication se fait par décalage vers la gauche et addition, donc une multiplication des
x * 0x01010101
résultatsx + (x<<8) + (x<<16) + (x<<24)
. Nos éléments 8 bits sont suffisamment larges (et contiennent des nombres suffisamment petits) pour que cela ne produise pas de report dans les 8 bits supérieurs.Une version 64 bits de ceci peut faire 8x éléments 8 bits dans un entier 64 bits avec un multiplicateur 0x010101010101010101 et extraire l'octet haut avec
>>56
. Il ne prend donc pas d'étapes supplémentaires, juste des constantes plus larges. C'est ce que GCC utilise__builtin_popcountll
sur les systèmes x86 lorsque l'popcnt
instruction matérielle n'est pas activée. Si vous pouvez utiliser des fonctions intégrées ou intrinsèques à cette fin, faites-le pour donner au compilateur la possibilité d'effectuer des optimisations spécifiques à la cible.Avec SIMD complet pour des vecteurs plus larges (par exemple, compter un tableau entier)
Cet algorithme bit à bit-SWAR pourrait se paralléliser pour être fait dans plusieurs éléments vectoriels à la fois, plutôt que dans un seul registre entier, pour une accélération sur les CPU avec SIMD mais sans instruction de popcount utilisable. (par exemple, le code x86-64 qui doit s'exécuter sur n'importe quel processeur, pas seulement Nehalem ou version ultérieure.)
Cependant, la meilleure façon d'utiliser les instructions vectorielles pour popcount est généralement d'utiliser un shuffle variable pour effectuer une recherche de table sur 4 bits à la fois de chaque octet en parallèle. (Les 4 bits indexent une table à 16 entrées contenue dans un registre vectoriel).
Sur les processeurs Intel, l'instruction popcnt matérielle 64 bits peut surpasser une implémentation parallèle-bit SSSE3
PSHUFB
d'environ un facteur 2, mais uniquement si votre compilateur l'obtient parfaitement . Sinon, l'ESS peut sortir nettement en tête. Les versions de compilateur plus récentes sont conscientes du problème de fausse dépendance popcnt sur Intel .Références:
la source
unsigned int
pour montrer facilement qu'il est exempt de toute complication de bit de signe. Seraituint32_t
également plus sûr, comme dans, vous obtenez ce que vous attendez sur toutes les plateformes?>>
est défini par l'implémentation pour les valeurs négatives. L'argument doit être modifié (ou converti) enunsigned
, et puisque le code est spécifique à 32 bits, il devrait probablement être utiliséuint32_t
.Tenez également compte des fonctions intégrées de vos compilateurs.
Sur le compilateur GNU par exemple, vous pouvez simplement utiliser:
Dans le pire des cas, le compilateur générera un appel à une fonction. Dans le meilleur des cas, le compilateur émettra une instruction cpu pour effectuer le même travail plus rapidement.
Les intrinsèques GCC fonctionnent même sur plusieurs plates-formes. Popcount deviendra courant dans l'architecture x86, il est donc logique de commencer à utiliser l'intrinsèque maintenant. D'autres architectures ont le popcount depuis des années.
Sur x86, vous pouvez indiquer au compilateur qu'il peut assumer la prise en
popcnt
charge des instructions avec-mpopcnt
ou-msse4.2
pour activer également les instructions vectorielles ajoutées dans la même génération. Voir les options de GCC x86 .-march=nehalem
(ou-march=
quel que soit le processeur que vous voulez que votre code assume et ajuste) pourrait être un bon choix. L'exécution du binaire résultant sur un processeur plus ancien entraînera une erreur d'instruction illégale.Pour rendre les binaires optimisés pour la machine sur laquelle vous les construisez, utilisez
-march=native
(avec gcc, clang ou ICC).MSVC fournit un intrinsèque pour l'
popcnt
instruction x86 , mais contrairement à gcc, c'est vraiment un intrinsèque pour l'instruction matérielle et nécessite un support matériel.Utilisation
std::bitset<>::count()
au lieu d'un intégréEn théorie, tout compilateur qui sait comment effectuer un décompte efficace pour le processeur cible doit exposer cette fonctionnalité via ISO C ++
std::bitset<>
. En pratique, vous pourriez être mieux avec le bit-hack ET / shift / ADD dans certains cas pour certains CPU cibles.Pour les architectures cibles où le popcount matériel est une extension facultative (comme x86), tous les compilateurs n'en ont pas qui en tirent
std::bitset
parti lorsqu'ils sont disponibles. Par exemple, MSVC n'a aucun moyen d'activer lapopcnt
prise en charge au moment de la compilation et utilise toujours une recherche de table , même avec/Ox /arch:AVX
(ce qui implique SSE4.2, bien que techniquement il y ait un bit de fonctionnalité distinct pourpopcnt
.)Mais au moins, vous obtenez quelque chose de portable qui fonctionne partout, et avec gcc / clang avec les bonnes options cibles, vous obtenez un popcount matériel pour les architectures qui le prennent en charge.
Voir asm de gcc, clang, icc et MSVC sur l'explorateur du compilateur Godbolt.
x86-64
gcc -O3 -std=gnu++11 -mpopcnt
émet ceci:PowerPC64
gcc -O3 -std=gnu++11
émet (pour laint
version arg):Cette source n'est pas spécifique à x86 ou spécifique à GNU, mais se compile bien uniquement pour x86 avec gcc / clang / icc.
Notez également que le remplacement de gcc pour les architectures sans popcount à instruction unique est une recherche de table octet par octet. Ce n'est pas merveilleux pour ARM, par exemple .
la source
std::bitset::count
. après avoir inséré cela compile en un seul__builtin_popcount
appel.À mon avis, la "meilleure" solution est celle qui peut être lue par un autre programmeur (ou le programmeur d'origine deux ans plus tard) sans commentaires abondants. Vous voudrez peut-être la solution la plus rapide ou la plus intelligente que certains aient déjà fournie, mais je préfère la lisibilité à l'intelligence à tout moment.
Si vous voulez plus de vitesse (et en supposant que vous le documentiez bien pour aider vos successeurs), vous pouvez utiliser une recherche de table:
Bien que ceux-ci dépendent de tailles de types de données spécifiques, ils ne sont donc pas portables. Mais, comme de nombreuses optimisations de performances ne sont pas portables de toute façon, cela peut ne pas être un problème. Si vous voulez la portabilité, je m'en tiendrai à la solution lisible.
la source
if ((value & 1) == 1) { count++; }
parcount += value & 1
?Extrait de Hacker's Delight, p. 66, figure 5-2
Exécute en instructions de ~ 20-ish (dépend de l'arc), pas de branchement.
Hacker's Delight est délicieux! Hautement recommandé.
la source
Integer.bitCount(int)
utilise cette même implémentation exacte.pop
au lieu depopulation_count
(oupop_cnt
si vous devez avoir une abréviation). @MarcoBolis Je suppose que cela sera vrai pour toutes les versions de Java, mais officiellement cela dépendra de l'implémentation :)Je pense que le moyen le plus rapide - sans utiliser de tables de recherche et de popcount - est le suivant. Il compte les bits définis avec seulement 12 opérations.
Cela fonctionne parce que vous pouvez compter le nombre total de bits définis en divisant en deux moitiés, en comptant le nombre de bits définis dans les deux moitiés, puis en les additionnant. Aussi connu sous le nom de
Divide and Conquer
paradigme. Entrons dans les détails ..Le nombre de bits sur deux bits peut être
0b00
,0b01
ou0b10
. Essayons de travailler cela sur 2 bits.C'est ce qui était requis: la dernière colonne indique le nombre de bits définis dans chaque paire de deux bits. Si le nombre à deux bits est
>= 2 (0b10)
alorsand
produit0b01
, sinon il produit0b00
.Cette déclaration doit être facile à comprendre. Après la première opération, nous avons le nombre de bits définis dans tous les deux bits, maintenant nous résumons ce nombre dans tous les 4 bits.
Nous résumons ensuite le résultat ci-dessus, en nous donnant le nombre total de bits définis sur 4 bits. La dernière affirmation est la plus délicate.
Décomposons-le plus loin ...
C'est similaire à la deuxième déclaration; nous comptons plutôt les bits définis par groupes de 4. Nous savons - en raison de nos opérations précédentes - que chaque quartet contient le nombre de bits définis. Regardons un exemple. Supposons que nous ayons l'octet
0b01000010
. Cela signifie que le premier quartet a son jeu de 4 bits et le second a son jeu de 2 bits. Maintenant, nous ajoutons ces grignotages ensemble.Il nous donne le nombre de bits définis dans un octet, dans le premier quartet
0b01100010
et donc nous masquons les quatre derniers octets de tous les octets du nombre (en les éliminant).Désormais, chaque octet contient le nombre de bits définis. Nous devons les additionner tous ensemble. L'astuce consiste à multiplier le résultat par
0b10101010
lequel a une propriété intéressante. Si notre numéro a quatre octets,A B C D
il en résultera un nouveau numéro avec ces octetsA+B+C+D B+C+D C+D D
. Un nombre de 4 octets peut avoir un maximum de 32 bits, qui peuvent être représentés comme0b00100000
.Tout ce dont nous avons besoin maintenant est le premier octet qui a la somme de tous les bits définis dans tous les octets, et nous l'obtenons
>> 24
. Cet algorithme a été conçu pour les32 bit
mots mais peut être facilement modifié pour les64 bit
mots.la source
c =
il? On dirait qu'il devrait être éliminé. De plus, suggérez un jeu de paren supplémentaire A "((((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24" pour éviter certains avertissements classiques.popcount(int v)
etpopcount(unsigned v)
. Pour la portabilité, considérezpopcount(uint32_t v)
, etc. Vraiment comme la partie * 0x1010101.return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
nous n'avons donc pas besoin de compter les lettres pour voir ce que vous faites réellement (puisque vous avez supprimé la première0
, j'ai accidentellement pensé que vous aviez utilisé le mauvais motif de bits (inversé) comme masque - c'est jusqu'à ce que je note qu'il n'y a que 7 lettres et non 8).Je me suis ennuyé et j'ai chronométré un milliard d'itérations de trois approches. Le compilateur est gcc -O3. Le CPU est tout ce qu'ils mettent dans le Macbook Pro de 1ère génération.
Le plus rapide est le suivant, à 3,7 secondes:
La deuxième place revient au même code mais en recherchant 4 octets au lieu de 2 demi-mots. Cela a pris environ 5,5 secondes.
La troisième place revient à l'approche "d'addition latérale" qui a pris un peu de temps, qui a pris 8,6 secondes.
La quatrième place revient à __builtin_popcount () de GCC, à une honteuse 11 secondes.
L'approche de comptage un bit à la fois a été plus lente, et je me suis ennuyé d'attendre qu'elle se termine.
Donc, si vous vous souciez de la performance avant tout, utilisez la première approche. Si vous vous en souciez, mais pas assez pour y dépenser 64 Ko de RAM, utilisez la deuxième approche. Sinon, utilisez l'approche un bit à la fois lisible (mais lente).
Il est difficile de penser à une situation dans laquelle vous voudriez utiliser l'approche du bit-twiddling.
Edit: Résultats similaires ici .
la source
S'il vous arrive d'utiliser Java, la méthode intégrée le
Integer.bitCount
fera.la source
Permettez-moi d'expliquer cet algorithme.
Cet algorithme est basé sur l'algorithme Divide and Conquer. Supposons qu'il existe un entier 8 bits 213 (11010101 en binaire), l'algorithme fonctionne comme ceci (à chaque fois fusionnez deux blocs voisins):
la source
C'est l'une de ces questions où il est utile de connaître votre micro-architecture. Je viens de chronométrer deux variantes sous gcc 4.3.3 compilées avec -O3 en utilisant les lignes C ++ pour éliminer la surcharge des appels de fonction, un milliard d'itérations, en gardant la somme cumulée de tous les décomptes pour garantir que le compilateur ne supprime rien d'important, en utilisant rdtsc pour le timing ( cycle d'horloge précis).
Le Hacker's Delight non modifié a pris 12,2 gigacycles. Ma version parallèle (comptant deux fois plus de bits) fonctionne en 13,0 gigacycles. 10,5 s au total se sont écoulés pour les deux ensemble sur un Core Duo à 2,4 GHz. 25 gigacycles = un peu plus de 10 secondes à cette fréquence d'horloge, donc je suis sûr que mes horaires sont corrects.
Cela a à voir avec les chaînes de dépendance des instructions, qui sont très mauvaises pour cet algorithme. Je pourrais presque doubler à nouveau la vitesse en utilisant une paire de registres 64 bits. En fait, si j'étais intelligent et ajoutais x + ya un peu plus tôt, je pourrais raser certains changements. La version 64 bits avec quelques petits ajustements serait à peu près égale, mais compterait à nouveau deux fois plus de bits.
Avec les registres SIMD 128 bits, encore un autre facteur de deux, et les jeux d'instructions SSE ont souvent aussi des raccourcis intelligents.
Il n'y a aucune raison pour que le code soit particulièrement transparent. L'interface est simple, l'algorithme peut être référencé en ligne à de nombreux endroits, et il se prête à un test unitaire complet. Le programmeur qui tombe dessus pourrait même apprendre quelque chose. Ces opérations de bits sont extrêmement naturelles au niveau de la machine.
OK, j'ai décidé de mettre la version 64 bits modifiée au banc. Pour cette taille unique (long non signé) == 8
Cela semble correct (je ne teste pas soigneusement, cependant). Maintenant, les timings sortent à 10,70 gigacycles / 14,1 gigacycles. Ce nombre ultérieur totalisait 128 milliards de bits et correspond à 5,9 secondes écoulées sur cette machine. La version non parallèle accélère un tout petit peu car je suis en mode 64 bits et elle aime les registres 64 bits légèrement mieux que les registres 32 bits.
Voyons voir s'il y a un peu plus de pipelines OOO à avoir ici. C'était un peu plus compliqué, donc j'ai testé un peu. Chaque terme totalise à lui seul 64, la somme combinée à 256.
J'étais excité pendant un moment, mais il s'avère que gcc joue des tours en ligne avec -O3 même si je n'utilise pas le mot-clé en ligne dans certains tests. Quand j'ai laissé gcc jouer des tours, un milliard d'appels à pop4 () prend 12,56 gigacycles, mais j'ai déterminé qu'il pliait les arguments comme des expressions constantes. Un nombre plus réaliste semble être de 19,6 gc pour une autre accélération de 30%. Ma boucle de test ressemble maintenant à ceci, en m'assurant que chaque argument est suffisamment différent pour empêcher gcc de jouer des tours.
256 milliards de bits additionnés en 8,17 secondes se sont écoulés. Fonctionne à 1,02 s pour 32 millions de bits comme indiqué dans la recherche de table 16 bits. Je ne peux pas comparer directement, car l'autre banc ne donne pas de vitesse d'horloge, mais il semble que j'ai supprimé la morve de l'édition de table de 64 Ko, ce qui est une utilisation tragique du cache L1 en premier lieu.
Mise à jour: décidé de faire l'évidence et de créer pop6 () en ajoutant quatre autres lignes dupliquées. Entré à 22,8 gc, 384 milliards de bits additionnés en 9,5 secondes se sont écoulés. Il y a donc encore 20% à 800 ms pour 32 milliards de bits.
la source
Pourquoi ne pas diviser itérativement par 2?
Je suis d'accord que ce n'est pas le plus rapide, mais "le meilleur" est quelque peu ambigu. Je dirais cependant que "le meilleur" devrait avoir un élément de clarté
la source
Le bit-twiddling de Hacker's Delight devient tellement plus clair lorsque vous écrivez les motifs de bits.
La première étape ajoute les bits pairs aux bits impairs, produisant une somme de bits dans chacun d'eux. Les autres étapes ajoutent des morceaux d'ordre élevé aux morceaux d'ordre inférieur, doublant la taille du morceau jusqu'à ce que le décompte final prenne l'intégralité de l'intégralité.
la source
Pour un juste milieu entre une table de recherche 2 32 et une itération à travers chaque bit individuellement:
Depuis http://ctips.pbwiki.com/CountBits
la source
Cela peut être fait dans
O(k)
, oùk
est le nombre de bits défini.la source
n &= (n-1)
forme la plus succincte .Ce n'est pas la solution la plus rapide ou la meilleure, mais j'ai trouvé la même question à ma manière, et j'ai commencé à réfléchir et à réfléchir. enfin j'ai réalisé que cela peut être fait comme ça si vous obtenez le problème du côté mathématique, et dessinez un graphique, alors vous trouvez que c'est une fonction qui a une partie périodique, puis vous réalisez la différence entre les périodes ... donc Voici:
la source
def f(i, d={0:lambda:0, 1:lambda:1, 2:lambda:1, 3:lambda:2}): return d.get(i, lambda: f(i//4) + f(i%4))()
La fonction que vous recherchez est souvent appelée «somme latérale» ou «nombre de population» d'un nombre binaire. Knuth en parle dans le pré-fascicule 1A, pp11-12 (bien qu'il y ait une brève référence dans le volume 2, 4.6.3- (7).)
Le locus classicus est l'article de Peter Wegner "A Technique for Counting Ones in a Binary Computer", tiré des Communications de l'ACM , Volume 3 (1960) Numéro 5, page 322 . Il y donne deux algorithmes différents, l'un optimisé pour les nombres censés être "clairsemés" (c'est-à-dire qu'ils en ont un petit nombre) et l'autre pour le cas contraire.
la source
la source
Quelques questions ouvertes: -
nous pouvons modifier l'algo pour supporter le nombre négatif comme suit: -
maintenant pour surmonter le deuxième problème, nous pouvons écrire l'algo comme: -
pour une référence complète, voir:
http://goursaha.freeoda.com/Miscivers/IntegerBitCount.html
la source
Je pense que la méthode de Brian Kernighan sera également utile ... Elle passe par autant d'itérations qu'il y a de bits définis. Donc, si nous avons un mot de 32 bits avec uniquement le bit le plus élevé, il ne passera qu'une seule fois dans la boucle.
la source
J'utilise le code ci-dessous qui est plus intuitif.
Logique: n & (n-1) réinitialise le dernier bit défini de n.
PS: Je sais que ce n'est pas une solution O (1), quoique intéressante.
la source
O(ONE-BITS)
. Il s'agit bien de O (1) car il y a au plus 32 bits à un.Que voulez-vous dire par "meilleur algorithme"? Le code court ou le code jeûné? Votre code a l'air très élégant et il a un temps d'exécution constant. Le code est également très court.
Mais si la vitesse est le facteur majeur et non la taille du code, je pense que la suite peut être plus rapide:
Je pense que ce ne sera pas plus rapide pour une valeur 64 bits mais une valeur 32 bits peut être plus rapide.
la source
J'ai écrit une macro de comptage de bits rapide pour les machines RISC vers 1990. Elle n'utilise pas d'arithmétique avancée (multiplication, division,%), de récupération de mémoire (beaucoup trop lente), de branches (trop lente), mais elle suppose que le CPU a un Décalage en barillet 32 bits (en d'autres termes, >> 1 et >> 32 prennent le même nombre de cycles.) Il suppose que les petites constantes (telles que 6, 12, 24) ne coûtent rien à charger dans les registres, ou sont stockées dans les temporaires et réutilisé encore et encore.
Avec ces hypothèses, il compte 32 bits en environ 16 cycles / instructions sur la plupart des machines RISC. Notez que 15 instructions / cycles est proche d'une limite inférieure sur le nombre de cycles ou d'instructions, car il semble prendre au moins 3 instructions (masque, décalage, opérateur) pour réduire de moitié le nombre d'addends, donc log_2 (32) = 5, 5 x 3 = 15 instructions est une limite quasi-inférieure.
Voici un secret pour la première étape la plus complexe:
donc si je prends la 1ère colonne (A) ci-dessus, la décale de 1 bit vers la droite et la soustrais de AB, j'obtiens la sortie (CD). L'extension à 3 bits est similaire; vous pouvez le vérifier avec une table booléenne à 8 rangées comme la mienne ci-dessus si vous le souhaitez.
la source
si vous utilisez C ++, une autre option consiste à utiliser la métaprogrammation de modèle:
l'utilisation serait:
vous pouvez bien sûr étendre davantage ce modèle pour utiliser différents types (même la taille de bits à détection automatique) mais je l'ai gardé simple pour plus de clarté.
edit: oublié de mentionner que c'est bon car cela devrait fonctionner dans n'importe quel compilateur C ++ et il déroule simplement votre boucle pour vous si une valeur constante est utilisée pour le nombre de bits (en d'autres termes, je suis presque sûr que c'est la méthode générale la plus rapide tu trouveras)
la source
constexpr
cependant.J'aime particulièrement cet exemple du fichier de fortune:
Je l'aime mieux parce que c'est si joli!
la source
Java JDK1.5
Integer.bitCount (n);
où n est le nombre dont les 1 doivent être comptés.
vérifiez aussi,
la source
J'ai trouvé une implémentation du comptage de bits dans un tableau avec l'utilisation de l'instruction SIMD (SSSE3 et AVX2). Ses performances sont 2 à 2,5 fois supérieures à celles de la fonction intrinsèque __popcnt64.
Version SSSE3:
Version AVX2:
la source
J'utilise toujours cela dans la programmation compétitive et c'est facile à écrire et efficace:
la source
Il existe de nombreux algorithmes pour compter les bits définis; mais je pense que le meilleur est le plus rapide! Vous pouvez voir le détail sur cette page:
Bit Twiddling Hacks
Je suggère celui-ci:
Comptage des bits définis en mots de 14, 24 ou 32 bits à l'aide d'instructions 64 bits
Cette méthode nécessite un processeur 64 bits avec une division de module rapide pour être efficace. La première option ne prend que 3 opérations; la deuxième option prend 10; et la troisième option prend 15.
la source
Solution C # rapide utilisant un tableau pré-calculé de décomptes d'octets avec branchement sur la taille d'entrée.
la source
(0xe994 >>(k*2))&3
, sans accès à la mémoire ...Voici un module portable (ANSI-C) qui peut comparer chacun de vos algorithmes sur n'importe quelle architecture.
Votre CPU a 9 octets de bits? Pas de problème :-) Pour le moment, il implémente 2 algorithmes, l'algorithme K&R et une table de recherche par octets. La table de recherche est en moyenne 3 fois plus rapide que l'algorithme K&R. Si quelqu'un peut trouver un moyen de rendre portable l'algorithme "Hacker's Delight", n'hésitez pas à l'ajouter.
.
la source
ce que tu peux faire c'est
la logique derrière cela est que les bits de n-1 sont inversés par rapport au bit le plus à droite de n. si n = 6, c'est-à-dire 110, alors 5 est 101, les bits sont inversés par rapport au bit le plus à droite de n. Donc, si nous et ces deux, nous ferons le bit le plus à droite 0 à chaque itération et irons toujours au bit défini le plus à droite suivant, d'où le comptage du bit défini.
la source