2012-01-15 13 views
23

Tengo algunos datos que pueden ser representados por un tipo Integral sin signo y su valor más grande requiere 52 bits. AFAIK solo Integer, Int64 y Word64 cumplen estos requisitos.`Entero` vs` Int64` contra `Word64`

Toda la información que pude averiguar sobre esos tipos era que Integer está firmado y tiene un poco de tamaño ilimitado flotante, Int64 y Word64 son fijos y con y sin signo, respectivamente. Lo que coudn't era averiguar la información sobre la aplicación real de estos tipos:

  1. ¿Cuántos bits será un valor de 52 bits en realidad ocupar si se almacena como un Integer?

  2. ¿Es correcto que Int64 y Word64 le permiten almacenar datos de 64 bits y pesar exactamente 64 bits para cualquier valor?

  3. Son algunos de esos tipos más eficaces o preferibles por cualquier otro motivo que el tamaño, p. Ej. implementaciones de código nativo o optimizaciones relacionadas con instrucciones de procesador directo?

  4. 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?

+5

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. –

+0

¿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

+0

@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). :) –

Respuesta

22

¿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 newtypeInt64, 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.

+0

He obtenido mucha información útil de su respuesta, ¡gracias! Sin embargo, aún tengo algunas preguntas: 1. No estoy seguro de haberlo entendido correctamente: al evitar una rama de operaciones adicionales ¿implicaba rechazar 'Integer'? 2. ¿Considera que el uso de implementaciones de longitud fija será más eficaz que 'Entero 'en todos los casos? 3. ¿No vale la pena la economía [aunque cuestionable] de ~ 10 bits gratis? 4. Daniel Fischer, en el comentario a mi pregunta, sugirió usar 'Integer' para un mejor rendimiento en el modo de 32 bits, ¿qué piensas de eso? –

+0

@NikitaVolkov: He ampliado mi respuesta para responder a esto. Aunque no entiendo muy bien el n. ° 3: ningún valor ocupa menos de una palabra de máquina completa, y GMP también asigna espacio para los bignums en palabras de máquina. – ehird

+0

Gracias por el comentario "palabra de máquina". Tan embarazoso como es, pero siendo un programador autodidacta, no lo sabía. Me queda una última pregunta: ¿por qué prefieres Int64 a través de Word64? –