Chcę policzyć unikalne bloki przechowywane w pliku przy użyciu Haskell. Blok to po prostu kolejne bajty o długości 512, a plik docelowy ma co najmniej 1 GB.Efektywny pojemnik mapy haszującej w Haskell?
To jest moja pierwsza próba.
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
Działa, ale jestem sfrustrowany jego czasem wykonania i zużyciem pamięci. Especilly, gdy porównałem go z wersją C++, a nawet implementacją Pythona wymienioną poniżej, był wolniejszy 3 ~ 5x i zużywał 2 ~ 3x więcej miejsca w pamięci.
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)
myślałem, że to głównie ze względu na wdrożenie HashMap i spróbował inne implementacje takich jak hashmap
, hashtables
i unordered-containers
. Ale nie było żadnej zauważalnej różnicy.
Proszę, pomóż mi ulepszyć ten program.
Właściwie to, co mnie najbardziej interesuje, to wykorzystanie pamięci, nie mogę zrozumieć nadmiernego użycia pamięci przez Haskell. Na przykład. Gdy plik wejściowy zawierał tylko 600 MB niepowtarzalnych danych, zużywał około 1 GB pamięci lub więcej. W każdym razie, dziękuję za odpowiedź i linki do artykułów. Powinienem rozważyć użycie FFI. – comatose
@Comatose, to tylko GHC. Strategia odśmiecania GHC korzysta z kolektora kopiowania, który jest naprawdę szybki, ale ma 2 x obciążenie pamięci. – luqui