2013-09-02 65 views
9

Tôi đang plaing với các chức năng Haskell song song parpseq và tôi đã khám phá điều gì đó thú vị.Hiệu suất tính toán danh sách song song Haskell

ví dụ của tôi dựa trên các ví dụ từ cuốn sách Real World Haskell 's (Parallel programming in Haskell):

mã chung:

import Control.Parallel (par, pseq) 

-- <<sorting code goes here>> 

force :: [a] ->() 
force xs = go xs `pseq`() 
    where go (_:xs) = go xs 
      go [] = 1 

main = do 
    print $ take 10 $ parSort [0..1000000] 

Mã Phân loại 1 (lấy từ cuốn sách):

parSort :: (Ord a) => [a] -> [a] 
parSort (x:xs) = force greater `par` (force lesser `pseq` 
             (lesser ++ x:greater)) 
    where lesser = parSort [y | y <- xs, y < x] 
      greater = parSort [y | y <- xs, y >= x] 
parSort _   = [] 

mã phân loại 2 (biến thể của tôi tùy chỉnh):

parSort :: (Ord a) => [a] -> [a] 
parSort (x:xs) = force greater `par` (lesser ++ x:greater) 
    where lesser = parSort [y | y <- xs, y < x] 
      greater = parSort [y | y <- xs, y >= x] 
parSort _   = [] 

Compile & chạy với: ghc -O2 -threaded --make Main.hs && time ./Main +RTS -N8

là gì thú vị, biến thể của tôi là một chút nhanh hơn so với những cuốn sách một:

sorting code 1 - avg. 16 seconds 
sorting code 2 - avg. 14 seconds 

Tôi muốn hỏi bạn tại sao chúng ta có thể quan sát hành vi như vậy và nếu giải pháp của cuốn sách mang lại bất kỳ lợi ích nào cho tôi. Tôi rất muốn hiểu sâu sắc tại sao giải pháp này có thể hoạt động tốt hơn.

Trả lời

7

Tôi muốn nói đó là do biến thể tùy chỉnh của bạn không ép buộc phần đầu tiên của danh sách. Chúng ta hãy xem những gì xảy ra ở cấp cao nhất: Bạn buộc nửa bên phải của danh sách, nhưng không phải phần bên trái. Và khi bạn in 10 phần tử đầu tiên, bạn chỉ đánh giá lazily 10 phần tử đầu tiên của phần bên trái và phần còn lại của nó không được đánh giá.

Mặt khác, giải pháp từ cuốn sách buộc cả hai phần, vì vậy trước khi bạn in 10 phần tử đầu tiên, bạn đánh giá cả phần bên trái và bên phải.

Thay vì in 10 yếu tố đầu tiên, thử in cuối cùng, như

print $ last $ parSort data 

sau đó cả hai biến thể của thuật toán sẽ phải đánh giá toàn bộ danh sách. Hoặc buộc toàn bộ danh sách sau khi sắp xếp và trước khi in.


Note rằng sắp xếp [0..100000] với thuật toán này sẽ rất hiệu quả, bởi vì bạn luôn chọn trục tồi tệ nhất có thể và nên phải mất O (n^2) thời gian. Việc đo lường sẽ không mang lại kết quả ý nghĩa nào cả. Nếu bạn muốn nhận được kết quả tốt đẹp với thời gian O (n log n), hãy nạp thuật toán với dữ liệu giống như ngẫu nhiên. Bạn có thể tìm thấy một phương pháp đơn giản làm thế nào để tạo một hoán vị ngẫu nhiên here.

Lưu ý: Thay vì sử dụng time Tôi khuyên bạn nên sử dụng criterion để đo mã của bạn. Sau đó, bạn chỉ có thể đo lường các phần có liên quan của mã, ngoại trừ khởi tạo, v.v. và buộc dữ liệu đầu vào và đầu ra của bạn để bạn đo chính xác một phần mà bạn quan tâm.