¿Cuántos bits será un valor de 52 bits en realidad ocupar si se almacena como un Integer
?
Esto depende de la implementación. Con GHC, los valores que se ajustan 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 a la S#
constructor de Integer
:
data Integer = S# Int#
| J# Int# ByteArray#
valores más grandes (es decir, aquellos representados con J#
) se almacenan con GMP.
Estoy en lo correcto que Int64
y Word64
permitirá almacenar un conjunto de datos de 64 bits y pesan exactamente 64 bits para cualquier valor?
No del todo - Son en caja. Un Int64
es en realidad un puntero a un puntero no evaluado o un puntero de una palabra a una tabla de información más un valor entero de 64 bits. (Consulte GHC commentary para obtener más información.)
Si realmente quiere algo que garantice que sea de 64 bits, sin excepciones, puede usar un tipo sin casilla como Int64#
, pero recomiendo encarecidamente crear perfiles primero; los valores no compartidos son bastante dolorosos de usar. Por ejemplo, no puede usar tipos unboxed como argumentos para escribir constructores, por lo que no puede tener una lista de Int64#
s. También debe 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 el vector, con cosas de lujo como el paralelismo automático); almacenan los valores desempaquetados debajo del capó, pero te permiten trabajar con ellos en forma de caja. (Por supuesto, cada valor individual que saque aparecerá en recuadro.)
Son algunos de esos tipos más eficientes o preferibles por cualquier otro motivo que el tamaño, p. implementaciones de código nativo o optimizaciones relacionadas con instrucciones de procesador directo?
Sí; usar Integer
incurre en una bifurcación 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 es necesario, Int64#
.
Si está utilizando una máquina de 32 bits: Probablemente Integer
, ya que en 32-bit Int64
se emula con llamadas FFI a funciones GHC que probablemente no estén muy optimizadas, pero probaría ambas y las compararía. Con Integer
, obtendrás el mejor rendimiento en enteros pequeños, y GMP está muy optimizado, por lo que probablemente sea mejor en los más grandes de lo que piensas.
Puede seleccionar entre Int64
y Integer
en tiempo de compilación utilizando el preprocesador C (activado con {-# LANGUAGE CPP #-}
); Creo que sería fácil conseguir que Cabal controle un #define
en función del ancho de la palabra de la arquitectura de destino. Tenga cuidado, por supuesto, de que no son lo mismo; tendrá que tener cuidado para evitar "desbordamientos" en el código Integer
, y p. Int64
es una instancia de Bounded
pero Integer
no lo es. Podría ser más simple enfocar un solo ancho de palabra (y así escribir) para el rendimiento y vivir con el rendimiento más lento en el otro.
que sugeriría la creación de su propio tipo Int52
como envoltura sobre newtype
Int64
, o un envoltorio Word52
sobre Word64
- sólo debes elegir lo que mejor coincida con los datos, no debería haber ningún impacto en el rendimiento; si solo son bits arbitrarios, iría con Int64
, porque Int
es más común que Word
.
Puede definir todas las instancias de manejar envolver automáticamente (:info Int64
tratar en GHCi para averiguar qué casos querrá definir), y proporcionar operaciones "inseguros" que solo se aplican directamente bajo la newtype
para situaciones de rendimiento crítico donde sabes que no habrá ningún desbordamiento.
Luego, si no exporta el constructor newtype
, siempre puede cambiar la implementación de Int52
más tarde, sin cambiar el resto del 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; solo existen en tiempo de compilación.
Nota: En sistemas de 32 bits (en relación con GHC, Windows es de 32 bits, incluso 64 bits de Windows), la mayoría de las operaciones en 'Int64' o' Word64' (y las variantes no compartidas) se implementan como llamadas externas a funciones C. Eso significa que en sistemas de 32 bits, el uso de 'Integer' tiene una posibilidad realista de ser más rápido que los tipos de 64 bits (+ constructor) de ancho fijo. Si suficientes valores realmente encajan en 32 bits, es probable que eso suceda. –
¿Llamar a GMP es realmente más económico que una llamada FFI normal? Sin embargo, sí tiene sentido si la mayoría de los valores se ajustan a 32 bits. – ehird
@DanielFischer Una nota muy útil, Daniel. Definitivamente tendré que tomar esto en consideración. ¡Gracias! En realidad, como habrás adivinado, mi sistema tendrá un valor <= 32 bit ~ 62% del tiempo (32/52). :) –