2012-01-13 45 views
8

Tôi có chức năng frequencyBy mà tôi muốn song song. Ở đây sau một trường hợp thử nghiệm đơn giản:Cách sử dụng Chiến lược song song trong Haskell

import Control.Parallel.Strategies 
import Control.DeepSeq 
import System.Environment 

frequencyBy :: (a -> b -> Bool) -> [a] -> [b] -> [(a,Int)] 
frequencyBy f as bs = map 
    (\a ->(a, foldr (\b -> if f a b then (+) 1 else id) 0 bs)) as 

main :: IO() 
main = do 
    x:xs <- getArgs 
    let result = frequencyBy (==) [1::Int .. 10000] [1 .. (read x)] `using` 
       parList rdeepseq 
    print $ product $ map snd $ result 

Tôi muốn chạy map trong frequencyBy song song. Tôi đang cố gắng để đạt được điều này bằng cách sử dụng parList rdeepseq (tất cả các công cụ khác trong main chỉ là để đảm bảo không phải tất cả mọi thứ được tối ưu hóa đi). Tuy nhiên, điều này không làm việc, hai chủ đề làm gấp đôi công việc như một thread nào trong cùng một thời điểm. Tôi không hiểu những gì tôi đang làm sai ở đây.

+3

Nếu hai chuỗi chỉ hoạt động gấp đôi một luồng trong cùng một thời gian, điều đó có nghĩa là nó song song đúng không? – ehird

Trả lời

9

Có thể là phí trên đang làm chậm mọi thứ, tùy thuộc vào mức độ lớn x là; nếu công việc bạn đang làm trong mỗi tia lửa có thể so sánh với thời gian cần thiết để sinh ra từng tia lửa (và tất nhiên là có quá trình lên lịch, vv), thì bạn sẽ gặp phải vấn đề.

Bạn có thể thử parListChunk, ví dụ: parListChunk 64 rdeepseq; bạn sẽ phải thử nghiệm để tìm ra kích thước đoạn nào cần sử dụng. Trong khi chiến lược hiện tại của bạn đang tạo ra tia lửa cho mọi phần tử trong danh sách, thì parListChunk sẽ tạo ra tia lửa cho từng đoạn có kích thước nhất định trong danh sách và sử dụng chiến lược bạn chỉ định tuần tự trên từng phần tử đó.

Bằng cách này, foldr trong frequencyBy có thể làm chậm mọi thứ do tạo ra quá nhiều thunk; một cái gì đó như

frequencyBy :: (a -> b -> Bool) -> [a] -> [b] -> [(a,Int)] 
frequencyBy f as bs = map (\a -> (a, sum . map (const 1) . filter (f a) $ bs)) as 

nên khắc phục điều đó.

Tất nhiên, như thường lệ, hãy đảm bảo bạn đang biên dịch với -O2 và chạy với +RTS -N.

+0

Đây không phải là cùng một mã; chức năng của OP tương đương với 'tổng. map (const 1) $ filter (f a) bs' hoặc 'length $ filter (f a) bs', mặc dù không phải là cải tiến đối với tôi (và sử dụng' length' chậm hơn nhiều). –

+0

'parListChunk 2 rdeepseq' đã thực hiện thủ thuật và đảm bảo nó chỉ mất một nửa thời gian trên hai luồng (so với một luồng). Điều này có vẻ lạ mặc dù, tại sao sẽ đánh giá khối 1 cung cấp cho nhiều chi phí, trong khi khối 2 dẫn đến parallelisation hoàn hảo? – user362382

+0

Tôi đã sử dụng 'tổng. map (const 1) $ filter (f a) bs' trước đó, nhưng tôi phát hiện ra rằng việc hợp nhất nó thành 'foldr' là nhanh hơn. – user362382

7

Tôi nghĩ chủ nghĩa song song của bạn quá chi tiết. parList cố gắng đánh giá mọi phần tử song song và thực sự không có nhiều công việc cho bất kỳ phần tử nào.

Khi tôi thay đổi từ parList thành parListChunk 500 thời gian thực hiện tăng gần 50%; khi tôi đang sử dụng một máy tính lõi kép thì tốt như vậy.

Để tham khảo, tôi đã thử nghiệm với x=20000.

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