GHC sử dụng một loại kết hợp đa nhiệm ưu tiên và hợp tác trong triển khai đồng thời.
Ở cấp độ Haskell, điều này có vẻ được ưu tiên vì các luồng không cần phải mang lại một cách rõ ràng và dường như có thể bị gián đoạn bởi thời gian chạy bất cứ lúc nào. Nhưng ở mức thời gian chạy, các luồng "mang lại" bất cứ khi nào chúng phân bổ bộ nhớ. Vì hầu hết tất cả các luồng Haskell liên tục phân bổ, nên điều này thường hoạt động khá tốt.
Tuy nhiên, nếu một phép tính cụ thể có thể được tối ưu hóa thành mã không phân bổ, nó có thể trở nên không hợp tác ở cấp độ thời gian chạy và do đó không được ưu tiên ở cấp độ Haskell. Như @Carl đã chỉ ra, đây thực sự là -fomit-yields
lá cờ, được ngụ ý bởi -O2
điều đó cho phép điều này xảy ra:
-fomit-yields
Yêu cầu GHC bỏ qua kiểm tra heap khi không thực hiện phân bổ. Mặc dù điều này cải thiện kích thước nhị phân khoảng 5%, nhưng điều đó cũng có nghĩa là các luồng chạy trong các vòng không phân bổ chặt chẽ sẽ không được ưu tiên một cách kịp thời. Nếu điều quan trọng là luôn có thể làm gián đoạn các luồng như vậy, bạn nên tắt tối ưu hóa này. Cũng xem xét việc biên dịch lại tất cả các thư viện với việc tối ưu hóa này đã bị tắt, nếu bạn cần đảm bảo tính gián đoạn.
Rõ ràng, trong thời gian chạy một luồng (không có -threaded
cờ), điều này có nghĩa là một luồng hoàn toàn có thể bỏ đói tất cả các luồng khác. Ít rõ ràng hơn, điều tương tự có thể xảy ra ngay cả khi bạn biên dịch -threaded
và sử dụng +RTS -N
các tùy chọn. Vấn đề là một luồng không hợp tác có thể bỏ đói bộ lập lịch thời gian chạy . Nếu tại một thời điểm nào đó, luồng không hợp tác là luồng duy nhất hiện đang chạy, nó sẽ không bị gián đoạn và bộ lập lịch sẽ không bao giờ được chạy lại để xem xét lập lịch cho các luồng bổ sung, ngay cả khi chúng có thể chạy trên các luồng O / S khác.
Nếu bạn chỉ đang thử kiểm tra một số thứ, hãy thay đổi chữ ký fib
thành fib :: Integer -> Integer
. Vì Integer
nguyên nhân phân bổ, mọi thứ sẽ bắt đầu hoạt động trở lại (có hoặc không có -threaded
).
Nếu bạn gặp phải vấn đề này trong mã thực , thì giải pháp đơn giản nhất, là giải pháp được đề xuất bởi @Carl: nếu bạn cần đảm bảo tính gián đoạn của các luồng, mã sẽ được biên dịch -fno-omit-yields
, giữ cho các cuộc gọi của trình lập lịch trong mã không phân bổ . Theo tài liệu, điều này làm tăng kích thước nhị phân; Tôi cho rằng nó đi kèm với một hình phạt hiệu suất nhỏ, quá.
Ngoài ra, nếu tính toán đã có IO
, thì rõ ràng yield
ing trong vòng lặp tối ưu hóa có thể là một cách tiếp cận tốt. Đối với một tính toán thuần túy, bạn có thể chuyển đổi nó thành IO và yield
mặc dù thông thường bạn có thể tìm thấy một cách đơn giản để giới thiệu phân bổ lại. Trong hầu hết các tình huống thực tế, sẽ có một cách để chỉ giới thiệu "một vài" yield
hoặc phân bổ - đủ để làm cho luồng phản hồi trở lại nhưng không đủ để ảnh hưởng nghiêm trọng đến hiệu suất. (Ví dụ: nếu bạn có một số vòng lặp đệ quy lồng nhau yield
hoặc buộc phân bổ trong vòng lặp ngoài cùng.)
MVar
trạng thái dễ bị điều kiện chủng tộc. Tôi sẽ thực hiện lưu ý đó một cách nghiêm túc.