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
Était-ce utile?

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top