"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:
combien de bits une valeur de 52 bits occupera-t-elle si elle est stockée comme un
Integer
?ai-je raison que
Int64
etWord64
vous permet de stocker des données 64 bits et de peser exactement 64 bits pour n'importe quelle valeur?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?
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?
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
etWord64
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.