我寫了一個將64位Double轉換爲ByteString的函數(架構/類型安全性不是真的問題 - 現在讓我們假設Double是64位的Word)。雖然下面的函數運行良好,但我想知道是否有更快的方法將Double轉換爲ByteString。在下面的代碼中,將Word64解壓到Word8列表中,然後反向(使其成爲小端格式),然後打包到ByteString中。代碼如下:有效地將64位Double轉換爲ByteString
{-# 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
在Mac上的x86樣本GHCI輸出:
*Main> bprint $ encodeDouble 7234.4
"0x666666666642bc40"
雖然代碼似乎運作良好,我打算用它來發送之前編碼大量的重複值的成字節串它通過IPC。所以,如果有的話,我會很欣賞使它更快的指針。
在我看來,double必須解壓到Word8中,然後打包到ByteString中。因此,可能是原來的整體算法,不能提高很多。但是,使用更有效的解包/包裝功能可能會有所作爲,如果有的話。
EDIT1: 我剛剛發現在Mac(GHC 7.0.3)另一種併發症 - 上面的代碼不會GHC編譯,因爲這個錯誤的 - 我在GHCI到目前爲止測試:
$ 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'
所以,看起來我必須回退FFI(穀物/數據二進制ieee754包),直到這個錯誤得到修復,或者直到找到解決方法。看起來像有關GHC Ticket 4092。如果這是一個新的錯誤或其他錯誤,請糾正我。與標準基準 更新使用unsafeCoerce代碼修復編譯問題代碼如下:現在,我不能編譯:(
EDIT2。
{-# 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
]
]
標準輸出(部分):
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
在進一步的分析,大部分的瓶頸似乎是在unpack64。強制採取〜6ns的。unpack64花費〜195ns。拆開word64與作爲word8的列表在這裏相當昂貴。
我很好奇爲什麼你不想使用'穀物'的方法,它將核心中的少數幾行縮小爲鏈接的答案。一旦你開始處理列表,你將得到更昂貴的東西。 – acfoltzer
acfoltzer,好點。我終於想出了我應該尋找的東西(putWord64le實現)。那就是訣竅。請參閱下面的帖子。如果您對在哪裏尋找快速列表實施有任何建議,請告訴我。 – Sal