float - fromintegral haskell
`Integer` vs` Int64` vs `Word64` (1)
Tengo algunos datos que se pueden representar con un tipo Integral
sin signo y su valor más grande requiere 52 bits. Solo AFAIK Integer
, Int64
y Word64
satisfacen estos requisitos.
Toda la información que pude encontrar sobre esos tipos fue que Integer
está firmado y tiene un tamaño de bit ilimitado flotante, Int64
y Word64
están fijos y firmados y sin firma respectivamente. Lo que no pude averiguar fue la información sobre la implementación real de esos tipos:
¿Cuántos bits ocupará realmente un valor de 52 bits si se almacena como un
Integer
?¿Estoy
Int64
queInt64
yWord64
permiten almacenar datos de 64 bits y pesar exactamente 64 bits para cualquier valor?¿Alguno de esos tipos es más eficaz o preferible por cualquier otro motivo que no sea el tamaño, por ejemplo, implementaciones de código nativo o optimizaciones relacionadas con las instrucciones del procesador directo?
Y por si acaso: ¿cuál recomendaría para almacenar un valor de 52 bits en una aplicación extremadamente sensible en términos de rendimiento?
¿Cuántos bits ocupará realmente un valor de 52 bits si se almacena como un
Integer
?
Esto depende de la implementación. Con GHC, los valores que caben dentro de una palabra de máquina se almacenan directamente en un constructor de Integer
, por lo que si está en una máquina de 64 bits, debería ocupar la misma cantidad de espacio que un Int. Esto corresponde al constructor S#
de Integer
:
data Integer = S# Int#
| J# Int# ByteArray#
Los valores más grandes (es decir, aquellos representados con J#
) se almacenan con GMP .
¿Estoy
Int64
queInt64
yWord64
permiten almacenar datos de 64 bits y pesar exactamente 64 bits para cualquier valor?
No del todo, están en caja . Un Int64
es en realidad un puntero a un procesador no evaluado o un puntero de una palabra a una tabla de información más un valor entero de 64 bits. (Vea el comentario de GHC para más información.)
Si realmente desea algo que garantice que sea de 64 bits, sin excepciones, puede usar un tipo sin Int64#
como Int64#
, pero recomendaría encarecidamente la creación de perfiles; Los valores sin caja son muy dolorosos de usar. Por ejemplo, no puede usar tipos sin caja como argumentos para escribir constructores, por lo que no puede tener una lista de Int64#
s. También tienes que usar operaciones específicas para enteros sin caja. Y, por supuesto, todo esto es extremadamente específico de GHC.
Si desea almacenar una gran cantidad de enteros de 52 bits, es posible que desee utilizar vector o repa (construido en vectores, con elementos sofisticados como el paralelismo automático); almacenan los valores sin caja debajo del capó, pero le permiten trabajar con ellos en forma de caja. (Por supuesto, cada valor individual que saque estará en caja).
¿Alguno de esos tipos es más eficaz o preferible por cualquier otro motivo que no sea el tamaño, por ejemplo, implementaciones de código nativo o optimizaciones relacionadas con las instrucciones del procesador directo?
Sí; el uso de Integer
incurre en una rama para cada operación, ya que tiene que distinguir los casos de máquina-palabra y bignum; Y, por supuesto, tiene que manejar el desbordamiento. Los tipos integrales de tamaño fijo evitan esta sobrecarga.
Y por si acaso: ¿cuál recomendaría para almacenar un valor de 52 bits en una aplicación extremadamente sensible en términos de rendimiento?
Si está utilizando una máquina de 64 bits: Int64
o, si debe Int64#
, Int64#
.
Si está utilizando una máquina de 32 bits: Probablemente Integer
, ya que en Int64
32 bits se emula con llamadas de FFI a funciones de GHC que probablemente no están muy optimizadas, pero probaría ambas y la compararía. Con Integer
, obtendrá el mejor rendimiento en enteros pequeños, y GMP está fuertemente optimizado, por lo que probablemente se desempeñará mejor en los más grandes de lo que cree.
Puede seleccionar entre Int64
e Integer
en tiempo de compilación utilizando el preprocesador C (habilitado con {-# LANGUAGE CPP #-}
); Creo que sería fácil lograr que Cabal controle un #define
basado en el ancho de palabra de la arquitectura de destino. Tenga cuidado, por supuesto, que no son lo mismo; tendrá que tener cuidado de evitar "desbordamientos" en el código Integer
y, por ejemplo, Int64
es una instancia de Bounded
pero Integer
no lo es. Podría ser más simple apuntar a un solo ancho de palabra (y, por lo tanto, escribir) para el rendimiento y vivir con el rendimiento más lento en el otro.
Le sugiero que cree su propio tipo Int52
como un newtype
envoltorio sobre Int64
, o un envoltorio Word64
sobre Word64
; simplemente elija la que mejor se adapte a sus datos, no debería haber un impacto en el rendimiento; si solo fueran bits arbitrarios, iría con Int64
, solo porque Int
es más común que Word
.
Puede definir todas las instancias para manejar el :info Int64
automáticamente (pruebe :info Int64
en GHCi para averiguar qué instancias desea definir), y proporcione operaciones "inseguras" que solo se aplican directamente bajo el newtype
para situaciones críticas de rendimiento en las que Sé que no habrá ningún desbordamiento.
Luego, si no exporta el constructor newtype
, siempre puede intercambiar la implementación de Int52
más tarde, sin cambiar el resto de su código. No se preocupe por la sobrecarga de un tipo separado: la representación en tiempo de ejecución de un newtype
es completamente idéntica al tipo subyacente; sólo existen en tiempo de compilación.