Popcount rapide en Ruby

Lorsque j'envisageais ce que je voulais implémenter, dans le processus, il est devenu nécessaire de "compter les bits avec l'entier 1". Cependant, j'ai remarqué qu'il était assez lent à implémenter dans Ruby, j'ai donc décidé de l'implémenter en langage C etc. (jkr2255 / bit_counter).

Cas précédent

Récemment, c'est devenu une instruction CPU appelée POPCNT (décrite plus tard), et quand j'ai pensé que quelqu'un l'avait implémentée, j'ai réalisé que, étonnamment, Bignum était presque intacte.

"Si vous n'en avez pas, vous pouvez le fabriquer vous-même!"

À propos des nombres négatifs

Dans le cas de Ruby, l'entier autorise des chiffres infinis (conceptuellement), et le nombre négatif est une représentation complémentaire de 2, donc le bit de signe 1 est ** infiniment **. Comme prévu, compter le nombre de 1 dans cet état est "infini" et n'a pas de sens, donc quand il s'agit d'un nombre négatif, "comptez le nombre de 0 et renvoyez-le comme un moins". [^ 1].

Version JRuby ... Implémentée en Java

Si vous créez une extension en langage C, JRuby ne sera pas pris en charge. Cependant, dans ce cas, il y avait des méthodes telles que Long.bitCount () et BigInteger # bitCount () du côté Java, donc Call this. jkr_2255 / items / 33b1eb1b2d4099ca1c67) était presque terminé.

Cependant, si la construction de l'extension C commence par JRuby, une erreur se produira, alors ajoutez une branche conditionnelle pour voir s'il s'agit de JRuby avec Rakefile ou gemspec, et générez une Gem distincte pour JRuby à RubyGems J'ai dû pousser.

Créer une extension C

Les entiers Ruby sont divisés en Fixnum et Bignum, mais Fixnum peut facilement être un entier en langage C, vous devriez donc le faire. C'est

Ces deux.

Nombre de bits au niveau du langage C

Tout d'abord, vérifiez avec CPUID pour voir si l'instruction POPCNT introduite dans SSE 4.2 peut être utilisée, et si elle est valide, comptez avec POPCNT [^ 2]. Sinon, utilisez __builtin_popcountl () intégré à GCC, sinon utilisez une version manuscrite de la fonction count.

Convertit la valeur de Bignum en tableau

Si vous continuez l'opération avec Bignum, un objet sera créé à chaque fois, et la vitesse ne sortira pas du tout. Donc, une fois que vous convertissez de Bignum en un tableau numérique, il y a deux fonctions, rb_big_pack et rb_integer_pack, selon la version de Ruby, et celle qui est valide peut différer. De plus, lorsque la capacité du tampon de conversion est petite, ʻALLOCA est utilisé à la place de ʻALLOC pour éviter une surcharge pour la pile (en fait, cela seul est 30% plus rapide pour les petits nombres).

Si vous entrez dans le tableau, le reste compte, mais dans l'environnement Windows x64, long a une largeur de 32 bits, donc le pointeur est lu de sorte que l'instruction POPCNT 64 bits puisse être utilisée.

référence

Comparé au num.to_s (2) .count ('1') couramment utilisé, il était 5 à 20 fois plus rapide (sur les machines avec des instructions POPCNT).

Défis restants

Ichiou Rubinius fonctionne également, mais cela ne va pas beaucoup plus vite, donc j'envisage quelque chose qui ne peut pas être fait.

[^ 1]: Java BigInteger # bitCount () renvoie le nombre de bits qui sont "différents du bit de signe". Autrement dit, il renvoie également une valeur positive pour les nombres négatifs. [^ 2]: Il semble que ce sera un peu plus rapide si vous utilisez AVX etc. pour vous enthousiasmer, mais je n'en ai pas fait tellement.

Recommended Posts

Popcount rapide en Ruby
Lourd en rubis! ??
Triangle de sortie en Ruby
Types de variables dans ruby
ABC177-Résoudre E avec Ruby
Valider les jetons JWT dans Ruby
Écrire l'héritage de classe dans Ruby
Mettre à jour Ruby dans l'environnement Unicorn
Entiers qui sont unifiés en entiers dans Ruby 2.4
[Ruby] Gestion des exceptions dans les fonctions
Utilisez des variables ruby en javascript.
Multiplication dans un tableau Ruby
NCk mod p dans Ruby
Essayez d'implémenter Yuma dans Ruby
Encodage lors de l'accès à Windows + Ruby
Ruby on Rails compatible japonais-anglais i18n
Comment installer Bootstrap dans Ruby
Ecrire des clés et des valeurs dans Ruby
[Super Introduction] À propos des symboles dans Ruby
Hanachan en Ruby (manipulation non destructive de tableaux)
Informations sur la version openssl dans ruby OPENSSL_VERSION
Méthodes Ruby souvent utilisées dans Rails
Segfo Ruby en 2 lignes
Soyez prudent lorsque vous omettez le retour dans Ruby
J'ai essayé un problème de calendrier avec Ruby
Implémenter le poker petit à petit dans Ruby Part 2
Implémenter le poker petit à petit dans Ruby Part 1
Faire un tri à bulles et sélectionner le tri avec Ruby
Différences d'écriture en Ruby, PHP, Java, JS
[Note technique] Qu'est-ce que "inclure" dans Ruby?
Implémenter le poker petit à petit dans Ruby Part 4
Implémenter l'algorithme dans Ruby: Jour 1 - Division mutuelle euclidienne-
Implémentation de la "méthode de détection de circulation Floyd" dans Ruby
Implémenter le poker petit à petit dans Ruby Part 3
Ruby on Rails dans les espaces de codes Visual Studio
Résumé des hachages et symboles dans Ruby
Méthodes que j'ai trouvées utiles dans Ruby