"Integer" vs "Int64" vs "Word64"

j'ai des données qui peuvent être représentées par un unsigned Integral le type et sa plus grande valeur nécessite 52 bits. Autant que je sache seulement Integer,Int64 et Word64 satisfont à ces exigences.

Toutes les informations que j'ai pu trouver sur ces types est que Integer est signé et a un bit-size flottant illimité,Int64 et Word64 sont respectivement fixes et signées et non signées. Ce que je n'ai pas pu découvrir, c'est l'information sur la mise en œuvre effective de ces types:

  1. combien de bits une valeur de 52 bits occupera-t-elle si elle est stockée comme un Integer?

  2. ai-je raison que Int64 et Word64 vous permet de stocker des données 64 bits et de peser exactement 64 bits pour n'importe quelle valeur?

  3. L'un de ces types est-il plus performant ou préférable pour d'autres raisons que la taille, p.ex. implémentations de code natif ou instructions de traitement direct optimisations?

  4. et juste au cas où: lequel recommanderiez-vous pour stocker une valeur de 52 bits dans une application extrêmement sensible en termes de performances?

23
demandé sur Nikita Volkov 2012-01-16 00:15:20

1 réponses

combien de bits une valeur de 52 bits occupera-t-elle si elle est stockée comme un Integer?

cela dépend de la mise en oeuvre. Avec GHC, les valeurs qui correspondent à un mot machine sont stockées directement dans un constructeur de Integer, donc si vous êtes sur une machine 64 bits, il devrait prendre la même quantité d'espace comme un Int. Cela correspond à l' S# constructeur Integer:

data Integer = S# Int#
             | J# Int# ByteArray#

valeurs plus grandes (c.-à-d. celles représentées par J#) sont stockées GMP.

ai-je raison que Int64 et Word64 vous permet de stocker des données 64 bits et de peser exactement 64 bits pour n'importe quelle valeur?

Pas tout à fait, ils sont boîte. Int64 est en fait un pointeur vers un thunk non évalué ou un pointeur d'un mot vers un info table plus une valeur entière de 64 bits. (Voir GHC commentary pour plus d'informations.)

si vous êtes vraiment vous voulez quelque chose qui est garanti d'être 64 bits, pas d'exceptions, alors vous pouvez utiliser un type non encadré comme Int64#, mais je recommande fortement le profilage d'abord; les valeurs non encadrées sont assez douloureuses à utiliser. Par exemple, vous ne pouvez pas utiliser des types non encadrés comme arguments pour taper des constructeurs, donc vous ne pouvez pas avoir une liste de Int64#s. Vous devez également utiliser des opérations spécifiques de décaissement des entiers. Et, bien sûr, tout cela est extrêmement spécifique au GHC.

Si vous cherchez à stocker beaucoup de 52 bits entiers, vous pouvez utiliser vecteur ou aper (construit sur vectoriel, avec des choses fantaisistes comme le parallélisme automatique); ils stockent les valeurs non encadrées sous le capot, mais vous permettent de travailler avec eux dans la forme encadrée. (Bien sûr, chaque valeur individuelle que vous retirez sera encadrée.)

est-ce que l'un ou l'autre de ces types est plus performant ou préférable pour d'autres raisons que la taille, p.ex. implémentations de code natif ou instructions de traitement direct-liées optimisations?

Oui; a l'aide de Integer engage une branche pour chaque opération, puisqu'elle doit distinguer le mot-machine et les cas de bignum; et, bien sûr, elle doit gérer le débordement. Les types intégraux de taille fixe évitent cette surabondance.

et juste au cas où: lequel recommanderiez-vous pour stocker une valeur de 52 bits dans une application extrêmement sensible en termes de performances?

Si vous utilisez une machine 64 bits: Int64 ou, si vous devez, Int64#.

Si vous utilisez une machine 32 bits: Probablement Integer puisque c'est sur 32 bits Int64 est émulé par des appels FFI vers des fonctions GHC qui ne sont probablement pas très optimisées, mais je vais essayer les deux et le benchmark. Integer, Vous obtiendrez la meilleure performance sur les petits entiers, et GMP est fortement optimisé, donc il fera probablement mieux sur les plus grands que vous pourriez penser.

vous pouvez choisir entre Int64 et Integer à la compilation en utilisant le préprocesseur C (activé avec {-# LANGUAGE CPP #-}); je pense qu'il serait facile d'obtenir de la Cabale pour le contrôle d'un #define basé sur la largeur de mot de l'architecture cible. Attention, bien sûr, qu'ils ne sont pas les mêmes; vous devrez faire attention à éviter les" débordements " dans le Integer code, et, par exemple,Int64 est une instance de Bounded mais Integer ne l'est pas. Il pourrait être plus simple de simplement cibler une seule largeur de mot (et donc de taper) pour la performance et de vivre avec la performance plus lente sur le autre.

je suggère la création de votre propre Int52 tapez un newtype wrapper sur Int64 ou un Word52 wrapper sur Word64 - il suffit de choisir celui qui correspond le mieux à vos données, il ne devrait pas y avoir d'impact sur les performances; si c'est juste des bits arbitraires, je dirais Int64, juste parce que Int est plus commun que Word.

vous pouvez définir toutes les instances pour gérer l'enrubannage automatiquement (essayez :info Int64 dans GHCi pour savoir quelles instances vous voulez define), et fournissent des opérations "dangereuses" qui s'appliquent directement sous le newtype pour les situations critiques de performance où vous savez qu'il n'y aura pas de débordement.

Alors, si vous n'exportez pas le newtype constructeur, vous pouvez toujours échanger l'implémentation de Int52 plus tard, sans changer le reste de votre code. Ne vous souciez pas de la surbrillance d'un type séparé - la représentation runtime d'un newtype est complètement identique au type sous-jacent; ils ne exister au moment de la compilation.

22
répondu ehird 2012-01-16 15:11:23