2013-01-31 54 views
15

Tôi đang cố gắng che giấu đầu xung quanh các chiến lược song song. Tôi nghĩ rằng tôi hiểu những gì mà mỗi combinators làm, nhưng mỗi khi tôi cố gắng sử dụng chúng với hơn 1 lõi, chương trình chậm đáng kể.Chiến lược song song hiệu quả

Ví dụ một thời gian trở lại tôi đã cố gắng để tính toán biểu đồ (và từ họ từ duy nhất) từ ~ 700 tài liệu. Tôi nghĩ rằng việc sử dụng mức chi tiết cấp tệp sẽ ổn. Với -N4 tôi nhận được 1.70 số dư công việc. Tuy nhiên với -N1 nó chạy trong một nửa thời gian hơn là với -N4. Tôi không chắc câu hỏi này thực sự là gì, nhưng tôi muốn biết làm thế nào để quyết định nơi/khi nào/làm thế nào để song song và đạt được một số hiểu biết về nó. Làm thế nào điều này sẽ được song song để tăng tốc độ với lõi thay vì giảm?

import Data.Map (Map) 
import qualified Data.Map as M 
import System.Directory 
import Control.Applicative 
import Data.Vector (Vector) 
import qualified Data.Vector as V 
import qualified Data.Text as T 
import qualified Data.Text.IO as TI 
import Data.Text (Text) 
import System.FilePath ((</>)) 
import Control.Parallel.Strategies 
import qualified Data.Set as S 
import Data.Set (Set) 
import GHC.Conc (pseq, numCapabilities) 
import Data.List (foldl') 

mapReduce stratm m stratr r xs = let 
    mapped = parMap stratm m xs 
    reduced = r mapped `using` stratr 
    in mapped `pseq` reduced 

type Histogram = Map Text Int 

rootDir = "/home/masse/Documents/text_conversion/" 

finnishStop = ["minä", "sinä", "hän", "kuitenkin", "jälkeen", "mukaanlukien", "koska", "mutta", "jos", "kuitenkin", "kun", "kunnes", "sanoo", "sanoi", "sanoa", "miksi", "vielä", "sinun"] 
englishStop = ["a","able","about","across","after","all","almost","also","am","among","an","and","any","are","as","at","be","because","been","but","by","can","cannot","could","dear","did","do","does","either","else","ever","every","for","from","get","got","had","has","have","he","her","hers","him","his","how","however","i","if","in","into","is","it","its","just","least","let","like","likely","may","me","might","most","must","my","neither","no","nor","not","of","off","often","on","only","or","other","our","own","rather","said","say","says","she","should","since","so","some","than","that","the","their","them","then","there","these","they","this","tis","to","too","twas","us","wants","was","we","were","what","when","where","which","while","who","whom","why","will","with","would","yet","you","your"] 
isStopWord :: Text -> Bool 
isStopWord x = x `elem` (finnishStop ++ englishStop) 

textFiles :: IO [FilePath] 
textFiles = map (rootDir </>) . filter (not . meta) <$> getDirectoryContents rootDir 
    where meta "." = True 
     meta ".." = True 
     meta _ = False 

histogram :: Text -> Histogram 
histogram = foldr (\k -> M.insertWith' (+) k 1) M.empty . filter (not . isStopWord) . T.words 

wordList = do 
    files <- mapM TI.readFile =<< textFiles 
    return $ mapReduce rseq histogram rseq reduce files 
    where 
    reduce = M.unions 

main = do 
    list <- wordList 
    print $ M.size list 

Đối với các tệp văn bản, tôi đang sử dụng pdf được chuyển đổi thành tệp văn bản, nhưng với mục đích, hầu như mọi sách/sách từ dự án gutenberg đều nên làm.

Sửa: Thêm hàng nhập khẩu để kịch bản

+1

'histogram = foldr (\ k -> M.insertWith '(+) k 1) M.empty. bộ lọc (not. isStopWord). T.words' nên sử dụng 'foldl''. 'Foldr' xây dựng một đoạn sâu như danh sách dài trước khi nó có thể bắt đầu xây dựng' Bản đồ'. –

+3

Sẽ dễ dàng hơn nếu bạn trả lời một câu hỏi như vậy nếu bạn cung cấp một ví dụ nhỏ và đầy đủ. Nếu không tìm kiếm nhiều chi tiết: Bạn có chắc chắn rằng 'rseq' là arg đầu tiên của' mapReduce' là đủ để buộc mỗi đoạn công việc thực sự được thực hiện song song không? Số lượng công việc phải được thực hiện cho mỗi phần tử danh sách trong 'parMap' đủ lớn để đảm bảo độ chi tiết tốt của các tác vụ song song? Bạn đã thử chạy threadscope trên chương trình của bạn để xem những gì đang xảy ra trên mỗi lõi? Bạn đã thử chạy với '+ RTS -s' để xem có bao nhiêu thời gian dành cho việc thu gom rác không? – kosmikus

+0

kosmikus, bạn có ý nghĩa gì? Ngoài việc nhập khẩu, tập lệnh đó hoàn toàn có thể chạy được. Đối với rseq/rdeepseq, tôi đã thử với các kết hợp khác không có may mắn. Đối với parMap, tôi cũng đã thử bản đồ với parListChunk và parListN. Và đối với threadscope, có vẻ như đều đặn cả hành động lẫn gc. -s nói rằng đó là 60% thời gian làm việc, tốt hơn so với trường hợp -N1. – Masse

Trả lời

4

Trong thực tế, việc kết hợp các bộ phối hợp song song với nhau cũng có thể khó khăn. Những người khác đã đề cập đến việc làm cho mã của bạn nghiêm ngặt hơn để đảm bảo bạn thực sự đang hoạt động song song, điều này thực sự quan trọng.

Hai thứ có thể thực sự giết hiệu suất là rất nhiều bộ nhớ truyền tải và bộ sưu tập rác. Ngay cả khi bạn không sản xuất nhiều rác, rất nhiều các bộ truyền tải bộ nhớ gây áp lực nhiều hơn cho bộ nhớ cache CPU và cuối cùng là bus bộ nhớ của bạn sẽ trở thành cổ chai. Hàm isStopWord của bạn thực hiện rất nhiều so sánh chuỗi và phải duyệt qua danh sách được liên kết khá dài để thực hiện việc này. Bạn có thể tiết kiệm rất nhiều công việc bằng cách sử dụng loại nội tuyến Set hoặc thậm chí tốt hơn, loại HashSet từ gói unordered-containers (do chuỗi lặp lại so sánh có thể tốn kém, đặc biệt nếu chúng chia sẻ tiền tố chung).

import   Data.HashSet    (HashSet) 
import qualified Data.HashSet    as S 

... 

finnishStop :: [Text] 
finnishStop = ["minä", "sinä", "hän", "kuitenkin", "jälkeen", "mukaanlukien", "koska", "mutta", "jos", "kuitenkin", "kun", "kunnes", "sanoo", "sanoi", "sanoa", "miksi", "vielä", "sinun"] 
englishStop :: [Text] 
englishStop = ["a","able","about","across","after","all","almost","also","am","among","an","and","any","are","as","at","be","because","been","but","by","can","cannot","could","dear","did","do","does","either","else","ever","every","for","from","get","got","had","has","have","he","her","hers","him","his","how","however","i","if","in","into","is","it","its","just","least","let","like","likely","may","me","might","most","must","my","neither","no","nor","not","of","off","often","on","only","or","other","our","own","rather","said","say","says","she","should","since","so","some","than","that","the","their","them","then","there","these","they","this","tis","to","too","twas","us","wants","was","we","were","what","when","where","which","while","who","whom","why","will","with","would","yet","you","your"] 

stopWord :: HashSet Text 
stopWord = S.fromList (finnishStop ++ englishStop) 

isStopWord :: Text -> Bool 
isStopWord x = x `S.member` stopWord 

Thay thế chức năng isStopWord của bạn với phiên bản này thực hiện tốt hơn nhiều và quy mô tốt hơn nhiều (mặc dù chắc chắn không phải 1-1). Bạn cũng có thể xem xét sử dụng HashMap (từ cùng một gói) thay vì Map vì cùng một lý do, nhưng tôi đã không nhận được thay đổi đáng chú ý khi làm như vậy.

Một tùy chọn khác là tăng kích thước heap mặc định để thực hiện một số áp lực ngoài GC và để cho nó nhiều chỗ hơn để di chuyển mọi thứ xung quanh. Cung cấp mã biên dịch mặc định là 1GB (-H1G), tôi nhận được số dư GC là khoảng 50% trên 4 lõi, trong khi tôi chỉ nhận được ~ 25% mà không (nó cũng chạy ~ 30% nhanh hơn).

Với hai thay đổi này, thời gian chạy trung bình trên bốn lõi (trên máy của tôi) giảm từ ~ 10,5 xuống còn 3,5 giây. Có thể cho rằng, có chỗ để cải thiện dựa trên số liệu thống kê của GC (vẫn chỉ dành 58% thời gian làm công việc sản xuất), nhưng làm tốt hơn đáng kể có thể yêu cầu một sự thay đổi mạnh mẽ hơn cho thuật toán của bạn.

+3

Tôi đang mở để thay đổi quyết liệt. Điều này là dành cho tôi để tìm hiểu sau khi tất cả :) – Masse

4

Tôi nghĩ Daniel đã nhận nó đúng - các Data.Map và danh sách là một lười biếng cấu trúc dữ liệu; bạn nên sử dụng cả hai foldl ' insertWith' để đảm bảo công việc cho mỗi đoạn được thực hiện háo hức --- nếu không tất cả công việc sẽ bị trì hoãn đến phần tuần tự (giảm).

Ngoài ra, không rõ ràng là việc tạo ra tia lửa cho mỗi tệp là chi tiết chính xác, đặc biệt nếu kích thước tệp khác nhau đáng kể. Nếu đó có thể là trường hợp, nó sẽ là thích hợp hơn để nối các danh sách từ và chia thành các khối chẵn (xem phần kết hợp parListChunk).

Trong khi bạn đang ở đó, tôi cũng sẽ xem xét một số cạm bẫy của việc sử dụng IO (readFile) để mở nhiều tệp (systme thời gian chạy có thể bị xử lý bởi vì nó giữ chúng quá lâu).

+0

Như đã thấy từ bình luận của tôi, tôi đã thử parMap, parListN và parListChunk. Tất cả đều có các đặc tính hiệu suất tương tự. Thay đổi foldr thành foldl 'khiến số dư tăng lên> 2, nhưng tổng thời gian chạy đã tăng gần gấp đôi. – Masse

+0

Tôi đã đổi thành mã để foldr -> foldl 'và di chuyển mapreduce từ wordList sang biểu đồ. Tôi chia tập tin thành các dòng và trong mapreduce tôi sử dụng parListChunk stratm (100) xs. Tôi thành công gấp bốn lần thời gian chạy (từ ~ 70 giây đến 300 giây) – Masse

Các vấn đề liên quan