Escribí una función para convertir Double de 64 bits a ByteString (arquitectura/seguridad tipo no es realmente un problema, supongamos por ahora que el doble es Word de 64 bits). Si bien la función siguiente funciona bien, me pregunto si existe una forma más rápida de convertir Double en ByteString. En el código siguiente, hay un desempaquetado de Word64 en la lista de Word8, seguido de reverso (para hacerlo en formato little endian), y luego empaquetado en ByteString. El código es el siguiente:Conversión de Double de 64 bits a ByteString de manera eficiente
{-# LANGUAGE MagicHash #-}
import GHC.Prim
import GHC.Types
import GHC.Word
import Data.Bits (shiftR)
import Data.ByteString (pack, unpack)
import Data.ByteString.Internal (ByteString)
import Text.Printf (printf)
encodeDouble :: Double -> ByteString
encodeDouble (D# x) = pack $ reverse $ unpack64 $ W64# (unsafeCoerce# x)
unpack64 :: Word64 -> [Word8]
unpack64 x = map (fromIntegral.(shiftR x)) [56,48..0]
-- function to convert list of bytestring into hex digits - for debugging
bprint :: ByteString -> String
bprint x = ("0x" ++) $ foldl (++) "" $ fmap (printf "%02x") $ unpack x
main = putStrLn $ bprint $ encodeDouble 7234.4
Una salida GHCi muestra en Mac X 86:
*Main> bprint $ encodeDouble 7234.4
"0x666666666642bc40"
Mientras que el código parece funcionar bien, tengo la intención de usarlo para codificar gran cantidad de valores Double en ByteString antes de enviar sobre IPC. Por lo tanto, agradeceré sugerencias para hacerlo más rápido, si es que hay alguno.
Me parece que el doble se debe desempaquetar en Word8, y luego se empaqueta en ByteString. Entonces, puede ser el algoritmo general tal como es, no se puede mejorar mucho. Pero, usar una función desempaquetar/empacar más eficiente probablemente marcará la diferencia, si es que hay una.
EDIT1: acabo de descubrir otra complicación en Mac (GHC 7.0.3) - el código anterior no se compilará en GHC debido a este error - que estaba probando en GHCi hasta ahora:
$ ghc -O --make t.hs
[1 of 1] Compiling Main (t.hs, t.o)
/var/folders/_q/33htc59519b3xq7y6xv100z40000gp/T/ghc6976_0/ghc6976_0.s:285:0:
suffix or operands invalid for `movsd'
/var/folders/_q/33htc59519b3xq7y6xv100z40000gp/T/ghc6976_0/ghc6976_0.s:304:0:
suffix or operands invalid for `movsd'
Por lo tanto, parece que tengo que recurrir a FFI (paquete cereal/data-binary-ieee754) hasta que se solucione este error, o hasta que encuentre una solución alternativa. Parece relacionado con GHC Ticket 4092. Corrígeme si se trata de un nuevo error o de un error diferente. Por ahora, no puedo compilarlo :(
Edit2: Actualización del código para utilizar unsafeCoerce corrige el problema de la compilación de código a continuación con el Criterio de referencia:.
{-# LANGUAGE MagicHash #-}
import GHC.Prim
import GHC.Types
import GHC.Word
import Data.Bits (shiftR)
import Data.ByteString (pack, unpack)
import Data.ByteString.Internal (ByteString)
import Text.Printf (printf)
import Unsafe.Coerce
import Criterion.Main
--encodeDouble :: Double -> ByteString
encodeDouble x = pack $ reverse $ unpack64 $ unsafeCoerce x
unpack64 :: Word64 -> [Word8]
unpack64 x = map (fromIntegral.(shiftR x)) [56,48..0]
main = defaultMain [
bgroup "encodeDouble" [
bench "78901.234" $ whnf encodeDouble 78901.234
, bench "789.01" $ whnf encodeDouble 789.01
]
]
Criterio de salida (truncada):
estimating cost of a clock call...
mean is 46.09080 ns (36 iterations)
benchmarking encodeDouble/78901.234
mean: 218.8732 ns, lb 218.4946 ns, ub 219.3389 ns, ci 0.950
std dev: 2.134809 ns, lb 1.757455 ns, ub 2.568828 ns, ci 0.950
benchmarking encodeDouble/789.01
mean: 219.5382 ns, lb 219.0744 ns, ub 220.1296 ns, ci 0.950
std dev: 2.675674 ns, lb 2.197591 ns, ub 3.451464 ns, ci 0.950
en un análisis más detallado, la mayor parte del cuello de botella parece estar en unpack64. la coacción toma ~ 6ns. unpack64 toma ~ 195ns. Desembalaje del word64 como una lista de word8 es bastante caro aquí.
Tengo curiosidad de por qué no quieres usar el enfoque de 'cereal', que se reduce a unas pocas líneas en el Núcleo cuando las notas de respuesta vinculadas. Tan pronto como comiences a lidiar con listas, terminarás con algo mucho más caro. – acfoltzer
acfoltzer, buen punto. Finalmente descubrí lo que debería estar buscando (implementación putWord64le). Eso hizo el truco. Por favor, mira mi publicación a continuación. Si tiene alguna sugerencia sobre dónde buscar la implementación rápida de la lista, hágamelo saber. – Sal