Limitation de l'utilisation de la mémoire lors de la lecture des fichiers
-
04-10-2019 - |
Question
Je suis un débutant Haskell et je pensais que ce serait un bon exercice. j'ai un affectation où je dois lire le fichier dans un thread A, gérer les lignes de fichiers en fils b_i, puis fournir en sortie les résultats en fil C.
J'ai mis en œuvre jusque-là déjà, mais l'une des conditions est que nous ne peut pas faire confiance que les ajustements entiers de fichiers en mémoire. J'espérais que paresseux IO et garbage collector feraient pour moi, mais hélas l'utilisation de la mémoire ne cesse d'augmenter et la hausse.
Le fil de lecture (A) lit le fichier avec readFile
qui est alors compressé
les numéros de ligne et enveloppé dans Just. Ces lignes sont ensuite écrites à fermeture éclair
à Control.Concurrent.Chan
. Chaque fil du consommateur B a son propre canal.
Chaque consommateur lit son propre canal quand il a des données et si le regex matches, il est à leur propre délivrés canal de sortie respectif enroulé Peut-être à l'intérieur (en listes).
L'imprimante vérifie le canal de sortie de chacun des fils de B. Si aucun des les résultats (ligne) est rien, la ligne est imprimée. Depuis à ce stade il devrait y avoir aucune référence aux anciennes lignes, je pensais que les ordures collecteur serait en mesure de libérer ces lignes, mais hélas je semble être le mal ici.
Le fichier .lhs est ici: http://gitorious.org/hajautettujen- sovellusten-muodostamistekniikat / hajautettujen-sovellusten-muodostamistekniikat / blobs / maître / mgrep.lhs
La question est, comment puis-je limiter l'utilisation de la mémoire, ou permettre à la poubelle Collecteur pour supprimer les lignes.
Snippets selon demandé. Si tout va bien pas trop indentation mal détruite:)
data Global = Global {done :: MVar Bool, consumers :: Consumers}
type Done = Bool
type Linenum = Int
type Line = (Linenum, Maybe String)
type Output = MVar [Line]
type Input = Chan Line
type Consumers = MVar (M.Map ThreadId (Done, (Input, Output)))
type State a = ReaderT Global IO a
producer :: [Input] -> FilePath -> State ()
producer c p = do
liftIO $ Main.log "Starting producer"
d <- asks done
f <- liftIO $ readFile p
mapM_ (\l -> mapM_
(liftIO . flip writeChan l) c)
$ zip [1..] $ map Just $ lines f
liftIO $ modifyMVar_ d (return . not)
printer :: State ()
printer = do
liftIO $ Main.log "Starting printer"
c <- (fmap (map (snd . snd) . M.elems)
(asks consumers >>= liftIO . readMVar))
uniq' c
where head' :: Output -> IO Line
head' ch = fmap head (readMVar ch)
tail' = mapM_ (liftIO . flip modifyMVar_
(return . tail))
cont ch = tail' ch >> uniq' ch
printMsg ch = readMVar (head ch) >>=
liftIO . putStrLn . fromJust . snd . head
cempty :: [Output] -> IO Bool
cempty ch = fmap (any id)
(mapM (fmap ((==) 0 . length) . readMVar ) ch)
{- Return false unless none are Nothing -}
uniq :: [Output] -> IO Bool
uniq ch = fmap (any id . map (isNothing . snd))
(mapM (liftIO . head') ch)
uniq' :: [Output] -> State ()
uniq' ch = do
d <- consumersDone
e <- liftIO $ cempty ch
if not e
then do
u <- liftIO $ uniq ch
if u then cont ch else do
liftIO $ printMsg ch
cont ch
else unless d $ uniq' ch
La solution
Offres de programmation simultanées ordre d'exécution ne se définit sauf si vous forcez un vous-même avec MVAR et autres. Ainsi, il est probable que les bâtons de fil de producteur tous / la plupart des lignes du chan avant tout consommateur les lit au large et les transmet. Une autre architecture qui devrait répondre aux exigences est juste ont le thread A appeler le readfile paresseux et coller le résultat dans un Mvar. Ensuite, chaque thread consommateur prend le Mvar, lit une ligne, puis les remplace par Mvar avant de passer à gérer la ligne. Même alors, si le fil de sortie ne peut pas suivre, le nombre de lignes de correspondance stockées sur le chan il peut construire arbitrairement.
Qu'est-ce que vous avez est une architecture de poussée. Pour faire vraiment travailler dans un espace constant, penser en termes de régies par la demande. Trouver un mécanisme tel que les signaux de fil de sortie aux threads de traitement qu'ils doivent faire quelque chose, et de telle sorte que les fils de traitement du signal au fil de lecteur qu'ils doivent faire quelque chose.
Une autre façon de le faire est d'avoir chans de taille limitée à la place. - de sorte que les blocs de fil de lecture lorsque les fils de processeur ne sont pas pris, et donc les fils de processeur bloc lorsque le fil de sortie n'a pas rattrapé
Dans son ensemble, le problème, en fait, me rappelle de référence de Tim Bray widefinder, bien que les exigences sont quelque peu différentes. Dans tous les cas, il a conduit à une large débat sur la meilleure façon de mettre en œuvre multicoeur grep. Le grand punchline était que le problème est lié IO, et que vous voulez plusieurs threads de lecture sur les fichiers mmapped.
Voir ici pour plus que vous aurez besoin de savoir: http://www.tbray.org/ongoing/When/200x/2007/09/20/Wide-Finder