我想使用Haskell來計算存儲在文件中的唯一塊。 該塊只是連續字節,長度爲512,目標文件的大小至少爲1GB。Haskell中有效的哈希映射容器?
這是我的第一次嘗試。
import Control.Monad
import qualified Data.ByteString.Lazy as LB
import Data.Foldable
import Data.HashMap
import Data.Int
import qualified Data.List as DL
import System.Environment
type DummyDedupe = Map LB.ByteString Int64
toBlocks :: Int64 -> LB.ByteString -> [LB.ByteString]
toBlocks n bs | LB.null bs = []
| otherwise = let (block, rest) = LB.splitAt n bs
in block : toBlocks n rest
dedupeBlocks :: [LB.ByteString] -> DummyDedupe -> DummyDedupe
dedupeBlocks = flip $ DL.foldl' (\acc block -> insertWith (+) block 1 $! acc)
dedupeFile :: FilePath -> DummyDedupe -> IO DummyDedupe
dedupeFile fp dd = LB.readFile fp >>= return . (`dedupeBlocks` dd) . toBlocks 512
main :: IO()
main = do
dd <- getArgs >>= (`dedupeFile` empty) . head
putStrLn . show . (*512) . size $ dd
putStrLn . show . (*512) . foldl' (+) 0 $ dd
它的工作原理,但我對它的執行時間和內存使用感到沮喪。特別是當我與C++甚至Python實現相比較時,它的速度慢了3〜5倍,消耗了2〜3倍的內存空間。
import os
import os.path
import sys
def dedupeFile(dd, fp):
fd = os.open(fp, os.O_RDONLY)
for block in iter(lambda : os.read(fd, 512), ''):
dd.setdefault(block, 0)
dd[block] = dd[block] + 1
os.close(fd)
return dd
dd = {}
dedupeFile(dd, sys.argv[1])
print(len(dd) * 512)
print(sum(dd.values()) * 512)
我認爲這主要是由於HashMap的實現,並嘗試其他實現如hashmap
,hashtables
和unordered-containers
。 但沒有任何明顯的差異。
請幫我改進這個程序。
其實,我最關心的是內存使用情況,我無法理解Haskell hashmaps的過多內存使用情況。例如。當輸入文件包含600MB獨特數據時,它使用了大約1GB或更多的內存。無論如何,感謝您的答案和文章鏈接。我應該考慮使用FFI。 – comatose
@comatose,那只是GHC。 GHC垃圾收集策略使用複製收集器,它非常快速,但是具有2倍的內存開銷。 – luqui