10

Tôi đã thực hiện một số công việc tính toán chuyên sâu trong F #. Các hàm như Array.Parallel.map sử dụng Thư viện song song tác vụ .Net đã tăng tốc mã của tôi theo cấp số nhân cho một nỗ lực thực sự rất nhỏ.F # PSeq.iter dường như không sử dụng tất cả các lõi

Tuy nhiên, do lo ngại về bộ nhớ, tôi làm lại một phần mã của mình để có thể được đánh giá một cách lười biếng bên trong biểu thức trình tự (điều này có nghĩa là tôi phải lưu trữ và chuyển ít thông tin hơn). Khi nó đến thời gian để đánh giá tôi đã sử dụng:

// processor and memory intensive task, results are not stored 
let calculations : seq<Calculation> = seq { ...yield one thing at a time... } 

// extract results from calculations for summary data 
PSeq.iter someFuncToExtractResults results 

Thay vì:

// processor and memory intensive task, storing these results is an unnecessary task 
let calculations : Calculation[] = ...do all the things... 

// extract results from calculations for summary data 
Array.Parallel.map someFuncToExtractResults calculations 

Khi sử dụng bất kỳ chức năng Array.Parallel tôi có thể thấy rõ ràng tất cả các lõi trên máy tính của tôi đá vào bánh (~ Sử dụng CPU 100%). Tuy nhiên bộ nhớ bổ sung cần thiết có nghĩa là chương trình không bao giờ kết thúc.

Với phiên bản PSeq.iter khi tôi chạy chương trình, chỉ có khoảng 8% sử dụng CPU (và sử dụng RAM tối thiểu).

Vì vậy: Có lý do nào khiến phiên bản PSeq chạy chậm hơn nhiều không? Có phải vì đánh giá lười biếng không? Có một số công cụ "song song" ma thuật mà tôi đang thiếu không?

Cảm ơn,

nguồn lực khác, việc triển khai mã nguồn của cả hai (họ dường như sử dụng thư viện song song khác nhau trong NET):

https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/array.fs

https://github.com/fsharp/powerpack/blob/master/src/FSharp.PowerPack.Parallel.Seq/pseq.fs

EDIT: Thêm nhiều chi tiết về các ví dụ và chi tiết mã số

Code:

  • Seq

    // processor and memory intensive task, results are not stored 
    let calculations : seq<Calculation> = 
        seq { 
         for index in 0..data.length-1 do 
          yield calculationFunc data.[index] 
        } 
    
    // extract results from calculations for summary data (different module) 
    PSeq.iter someFuncToExtractResults results 
    
  • Mảng

    // processor and memory intensive task, storing these results is an unnecessary task 
    let calculations : Calculation[] = 
        Array.Parallel.map calculationFunc data 
    
    // extract results from calculations for summary data (different module) 
    Array.Parallel.map someFuncToExtractResults calculations 
    

chi tiết:

  • Các lưu trữ các intermediat Phiên bản e array chạy nhanh (theo như trước khi bị lỗi) dưới 10 phút nhưng sử dụng ~ 70GB RAM trước khi nó bị treo (64 GB vật lý, phần còn lại được phân trang)
  • Phiên bản seq chiếm hơn 34 phút và sử dụng một phần RAM (chỉ khoảng 30GB)
  • Có ~ một tỷ giá trị tôi đang tính toán. Do đó một tỷ đôi (với 64bits mỗi) = 7.4505806GB. Có nhiều dạng dữ liệu phức tạp hơn ... và một số bản sao không cần thiết tôi đang dọn dẹp do đó sử dụng RAM lớn hiện tại.
  • Có kiến ​​trúc không tuyệt vời, việc đánh giá lười biếng là phần đầu tiên của tôi cố gắng tối ưu hóa chương trình và/hoặc chuyển hàng loạt dữ liệu thành các đoạn nhỏ hơn
  • Với tập dữ liệu nhỏ hơn, cả hai đoạn mã đầu ra giống nhau các kết quả.
  • @pad, tôi đã thử những gì bạn đề xuất, PSeq.lặp lại dường như hoạt động đúng (tất cả các lõi hoạt động) khi được tính toán [], nhưng vẫn còn vấn đề RAM (nó cuối cùng đã bị rơi)
  • cả phần tóm tắt của mã và phần tính toán là CPU chuyên sâu (chủ yếu là vì các bộ dữ liệu lớn)
  • với phiên bản Seq tôi chỉ nhằm mục đích để parallelize lần
+1

Đánh giá lười biếng không hoạt động tốt với thực thi song song. Để công bằng, hãy chuyển cùng một 'Tính toán []' thành 'PSeq.iter' và' Array.Parallel.map'. Không thể nói lý do mà không có thêm chi tiết về 'Calculation' và' someFuncToExtractResults'. – pad

+0

Cảm ơn bạn đã gợi ý, tôi đã thử cách này và PSeq hoạt động tốt khi được đưa vào mảng thay thế trên thiết bị thu gọn ... tuy nhiên nó không giải quyết vấn đề RAM –

Trả lời

5

Dựa trên thông tin cập nhật của bạn, tôi rút ngắn câu trả lời của tôi để chỉ các phần có liên quan. Bạn chỉ cần điều này thay vì những gì bạn đang có:

let result = data |> PSeq.map (calculationFunc >> someFuncToExtractResults) 

Và điều này sẽ làm việc như nhau cho dù bạn sử dụng PSeq.map hoặc Array.Parallel.map.

Tuy nhiên, vấn đề thực sự của bạn sẽ không được giải quyết. Vấn đề này có thể được tuyên bố là: khi đạt được mức độ mong muốn của công việc song song để đạt được mức sử dụng CPU 100%, không có đủ bộ nhớ để hỗ trợ các quy trình.

Bạn có thể xem cách điều này sẽ không được giải quyết không? Bạn có thể xử lý mọi thứ liên tục (ít CPU hiệu quả hơn, nhưng hiệu quả bộ nhớ) hoặc bạn có thể xử lý mọi thứ song song (nhiều CPU hiệu quả hơn, nhưng hết bộ nhớ).

Các tùy chọn sau đó là:

  1. Thay đổi mức độ song song được sử dụng bởi các chức năng này để cái gì đó sẽ không thổi nhớ của bạn:

    let result = data 
          |> PSeq.withDegreeOfParallelism 2 
          |> PSeq.map (calculationFunc >> someFuncToExtractResults) 
    
  2. Thay đổi logic cơ bản cho calculationFunc >> someFuncToExtractResults để nó là một chức năng duy nhất hiệu quả hơn và truyền dữ liệu đến kết quả. Nếu không biết thêm chi tiết, không đơn giản để xem cách thực hiện điều này. Nhưng nội bộ, chắc chắn một số tải lười biếng có thể là có thể.

+0

Cả hai đều chuyên sâu, Tôi không chắc chắn ý của bạn là gì điểm, bạn có thể xây dựng xin vui lòng? –

+0

@AnthonyTruskinger: Tôi đã thực hiện một số cập nhật quan trọng dựa trên thông tin bổ sung bạn đã cung cấp. Lưu ý rằng bạn phải chọn một giao dịch ở đâu đó nếu bạn không muốn thay đổi thuật toán (bạn sẽ không nhận được 100% CPU và bộ nhớ hiệu quả mà không thay đổi thuật toán). Nếu bạn có thể thay đổi thuật toán, hãy xem câu trả lời của tôi. – yamen

3

Array.Parallel.map sử dụng Parallel.For dưới mui xe khi PSeq là một wrapper mỏng xung quanh PLINQ. Nhưng lý do họ cư xử khác nhau ở đây là không có đủ khối lượng công việc cho PSeq.iter khi seq<Calculation> là tuần tự và quá chậm trong việc cho ra kết quả mới.

Tôi không có ý tưởng sử dụng phương thức seq hoặc mảng trung gian. Giả sử data là mảng đầu vào, di chuyển tất cả các tính toán ở một nơi là con đường để đi:

// Should use PSeq.map to match with Array.Parallel.map 
PSeq.map (calculationFunc >> someFuncToExtractResults) data 

Array.Parallel.map (calculationFunc >> someFuncToExtractResults) data 

Bạn tránh tiêu thụ quá nhiều bộ nhớ và có tính chuyên sâu ở một nơi mà dẫn hiệu quả tốt hơn trong việc thực hiện song song.

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