Tôi đã sử dụng các kiểm tra từ StriplingWarror để tìm ra sự khác biệt xuất phát từ đâu.Tôi đã làm điều này bởi vì khi tôi nhìn với Reflector tại mã lớp Parallel không có gì khác hơn là tạo ra một loạt các nhiệm vụ và cho phép chúng chạy.
Từ quan điểm lý thuyết, cả hai phương pháp tiếp cận phải tương đương về thời gian chạy. Nhưng như các bài kiểm tra (không thực tế) với một hành động rỗng đã cho thấy rằng lớp Parallel nhanh hơn nhiều.
Phiên bản tác vụ dành hầu hết thời gian của mình bằng cách tạo các tác vụ mới dẫn đến nhiều bộ sưu tập rác. Sự khác biệt về tốc độ bạn thấy hoàn toàn là do bạn tạo ra nhiều nhiệm vụ nhanh chóng trở thành rác.
Lớp Parallel thay vì tạo ra lớp dẫn xuất nhiệm vụ của riêng nó mà chạy đồng thời trên tất cả các CPU. Chỉ có một nhiệm vụ phyiscal chạy ở tất cả các lõi. Việc đồng bộ hóa xảy ra bên trong nhiệm vụ ủy nhiệm bây giờ, nó giải thích tốc độ nhanh hơn nhiều của lớp Parallel.
ParallelForReplicatingTask task2 = new ParallelForReplicatingTask(parallelOptions, delegate {
for (int k = Interlocked.Increment(ref actionIndex); k <= actionsCopy.Length; k = Interlocked.Increment(ref actionIndex))
{
actionsCopy[k - 1]();
}
}, TaskCreationOptions.None, InternalTaskOptions.SelfReplicating);
task2.RunSynchronously(parallelOptions.EffectiveTaskScheduler);
task2.Wait();
Vậy điều gì tốt hơn? Nhiệm vụ tốt nhất là nhiệm vụ không bao giờ chạy. Nếu bạn cần tạo nhiều nhiệm vụ như vậy, chúng sẽ trở thành gánh nặng cho bộ thu gom rác, bạn nên tránh xa các API nhiệm vụ và dính vào lớp Parallel cho phép bạn thực hiện song song trực tiếp tại tất cả các lõi mà không có nhiệm vụ mới.
Nếu bạn cần nhanh hơn, có thể tạo chủ đề bằng tay và sử dụng cấu trúc dữ liệu được tối ưu hóa bằng tay để cung cấp cho bạn tốc độ tối đa cho mẫu truy cập của bạn là giải pháp hiệu suất nhất. Nhưng không chắc rằng bạn sẽ thành công khi làm như vậy vì các API TPL và Parallel đã được điều chỉnh rất nhiều. Thông thường, bạn cần phải sử dụng một trong nhiều quá tải để cấu hình các tác vụ đang chạy của bạn hoặc lớp Parallel để đạt được điều tương tự với mã ít hơn nhiều.
Nhưng nếu bạn có mô hình luồng không chuẩn, có thể bạn nên tắt mà không cần sử dụng TPL để tận dụng tối đa lõi của mình. Ngay cả Stephen Toub đã đề cập rằng các API TPL không được thiết kế cho hiệu suất cực nhanh nhưng mục tiêu chính là làm cho luồng dễ dàng hơn cho lập trình viên "trung bình". Để đánh bại TPL trong những trường hợp cụ thể, bạn cần phải ở trên mức trung bình và bạn cần phải biết nhiều thứ về các dòng bộ nhớ cache CPU, lập lịch trình luồng, mô hình bộ nhớ, tạo mã JIT, ... để xuất hiện trong kịch bản cụ thể của bạn tốt hơn.
Tôi nghĩ rằng chúng ít nhiều tương đương. Hồ sơ của bạn cho bạn biết điều gì? –
bạn đã xem xét điểm chuẩn chưa? –
Kiểm tra bài đăng nhỏ đẹp này về 2 phương pháp: http://blackrabbitcoder.net/archive/2012/12/20/c.net-little-wonders-the-parallel.invoke-method.aspx. Tôi không nghĩ rằng có bất kỳ sự khác biệt hiệu suất mặc dù, tôi nghĩ rằng Parallel.Invoke chỉ là dễ dàng hơn. –