2010-09-19 6 views
5

Tôi là người mới bắt đầu sử dụng Haskell và nghĩ rằng đây sẽ là bài tập tốt. Tôi có một bài tập nơi tôi cần đọc tệp trong chuỗi A, xử lý các dòng tệp trong chủ đề B_i, sau đó xuất kết quả trong chủ đề C.Hạn chế sử dụng bộ nhớ khi đọc các tệp

Tôi đã thực hiện điều này, nhưng một trong các yêu cầu là chúng tôi không thể tin tưởng rằng toàn bộ tệp phù hợp với bộ nhớ. Tôi đã hy vọng rằng lười biếng IO và thu gom rác sẽ làm điều này cho tôi, nhưng than ôi việc sử dụng bộ nhớ tiếp tục tăng và tăng.

Chuỗi trình đọc (A) đọc tệp với readFile sau đó được nén với số dòng và được bao bọc trong Chỉ. Những dòng được nén sau đó được viết đến Control.Concurrent.Chan. Mỗi chủ đề người tiêu dùng B có kênh riêng của mình.

Mỗi người tiêu dùng đọc kênh riêng của họ khi có dữ liệu và nếu regex kết quả phù hợp, kênh này được xuất cho kênh đầu ra tương ứng của riêng mình được bọc trong Có thể (được tạo thành danh sách).

Máy in kiểm tra kênh đầu ra của từng chuỗi B. Nếu không có kết quả (dòng) là Không có gì, dòng được in. Vì tại thời điểm này không nên có tham chiếu đến các dòng cũ hơn, tôi nghĩ rằng rác collector sẽ có thể phát hành những dòng này, nhưng tôi dường như ở trong sai ở đây.

File .lhs là ở đây: http://gitorious.org/hajautettujen-sovellusten-muodostamistekniikat/hajautettujen-sovellusten-muodostamistekniikat/blobs/master/mgrep.lhs

Vì vậy, câu hỏi là, làm thế nào để hạn chế việc sử dụng bộ nhớ, hoặc cho phép các bộ thu rác để loại bỏ các dòng.

Đoạn trích theo yêu cầu. Hy vọng rằng indenting không phải là quá xấu bị phá hủy :)

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 

Trả lời

6

Lập trình đồng thời cung cấp không có lệnh thực hiện được xác định trừ khi bạn thực thi một mình với mvars và muốn. Vì vậy, nó có khả năng rằng các chủ đề sản xuất gậy tất cả/hầu hết các dòng trong chan trước khi bất kỳ người tiêu dùng đọc chúng đi và vượt qua chúng trên. Một kiến ​​trúc khác phù hợp với yêu cầu là chỉ có một luồng. Gọi một tệp readfile lười và dán kết quả vào một mvar. Sau đó, mỗi thread tiêu thụ lấy mvar, đọc một dòng, sau đó thay thế mvar trước khi tiếp tục xử lý dòng. Thậm chí sau đó, nếu luồng đầu ra không thể theo kịp, thì số lượng các dòng khớp được lưu trữ trên chan có thể tùy ý xây dựng.

Điều bạn có là kiến ​​trúc đẩy. Để thực sự làm cho nó hoạt động trong không gian liên tục, hãy suy nghĩ về nhu cầu điều khiển. Tìm một cơ chế sao cho tín hiệu luồng đầu ra tới các luồng xử lý mà chúng phải làm một cái gì đó, và sao cho các luồng xử lý báo hiệu cho luồng đọc mà chúng nên làm điều gì đó.

Một cách khác để thực hiện điều này là thay đổi kích thước giới hạn - do đó luồng của trình đọc chặn khi chủ đề bộ xử lý không bắt kịp và do đó luồng bộ xử lý chặn khi luồng đầu ra không bắt kịp.

Nói chung, sự cố trên thực tế làm tôi nhớ đến chuẩn mực kính rộng của Tim Bray, mặc dù các yêu cầu có phần khác nhau. Trong mọi trường hợp, nó đã dẫn đến một cuộc thảo luận rộng rãi về cách tốt nhất để thực hiện grep đa lõi. Các punchline lớn là vấn đề là IO bị ràng buộc, và bạn muốn nhiều chủ đề đọc trên các tập tin mmapped.

Xem ở đây để biết thêm hơn bao giờ bạn sẽ muốn biết: http://www.tbray.org/ongoing/When/200x/2007/09/20/Wide-Finder

+4

BoundedChan là trên hackage cho chính xác loại hình này sử dụng. –

+0

Cảm ơn Tom và sciv. Tôi sẽ thử triển khai và đánh dấu câu trả lời nếu nó hoạt động – Masse