Trang chủ > Uncategorized > Khác biệt giữa Parallel.ForEach() và ParallelEnumerable.ForAll() trong .NET framework

Khác biệt giữa Parallel.ForEach() và ParallelEnumerable.ForAll() trong .NET framework

Tháng Mười Một 19, 2010 Để lại bình luận Go to comments

Parallel.ForEach() là một static method cho phép thực hiện song song các thao tác trên một Enumerable object. ParallelEnumerable.ForAll() là một extension method cho phép thực hiện song song các thao tác trên một Enumerable object do một câu truy vấn tạo ra (hơi khó giải thích). Tuy nhiên, không chỉ khác biệt về cách sử dụng, chúng cũng có khác biệt về performance nói chung.

Giả sử bạn gặp tình huống xử lí trên một biến data kiểu Enumerable như sau:

var q = from d in data … select d;
foreach (var item in q) { doSomething(item); }

Nếu bạn muốn câu truy vấn q chạy ở chế độ song song (chẳng hạn, khi bạn có nhiều core và muốn tận dụng chúng) bạn dùng ParallelEnumerable.AsParallel():

var q = from d in data.AsParallel() … select d;
foreach (var item in q) { doSomething(item); }

Và nếu hàm doSomething() không quan tâm đến thứ tự xử lí của item (tức là gọi doSomething với item nào trước thì kết quả cũng ra như nhau) thì bạn cũng có thể chạy foreach ở chế độ song song:

var q = from d in data.AsParallel() … select d;
Parallel.ForEach(q, item => { doSomething(item) });

Nhưng bạn cũng có thể dùng ForAll với câu truy vấn q để đạt hiệu quả tương tự:

var q = (from d in data.AsParallel() … select d);
q.ForAll(item => { doSomething(item) });

Nhìn hai đoạn code cuối ta có cảm giác performance của chúng giống nhau, nhưng thực ra không phải như vậy. Tạm gọi đoạn code dùng Parallel.ForEach() là đoạn code (1) và đoạn code dùng ParallelEnumerable.ForAll() là đoạn code (2).

Các công việc mà đoạn code (1) thực hiện (không hoàn toàn chính xác trên thực tế, nhưng đủ để cho thấy sự khác biệt) là:

  • Chạy câu truy vấn:
    • Phân hoạch data ra thành nhiều phần (để chạy song song).
    • Chạy câu truy vấn trên từng data, tạm gọi kết quả là q1, q2, q3, v.v.
    • Trộn q1, q2, q3, v.v lại thành Enumerable object q.
  • Chạy Parallel.ForEach():
    • Phân hoạch q ra thành nhiều phần (để chạy song song).
    • Gọi doSomething();

Sơ đồ minh họa cho đoạn code (1).

image

Đoạn code (1) lãng phí ở chỗ đầu tiên ta trộn các q1, q2, q3, v.v lại thành q, sau đó lại phân hoạch q ra. Sẽ tốt hơn nếu sau khi phân hoạch data, câu truy vấn được chạy, và các item lấy ra sẽ được cho vào doSomething() luôn. Nói cách khác doSomething() trở thành một phần trong hoạt động của câu truy vấn. Đó chính là cách mà đoạn code (2) thi hành. Dưới đây là sơ đồ minh họa cho đoạn code (2):

image

Đó là lí do lại sao đoạn code (2) cho performance tốt hơn, và được khuyên dùng trong những tình huống tương tự.

Nếu muốn tìm hiểu thêm về Parallel trong C#, bạn có thể đọc Paterns of Parallel Programming (sách miễn phí từ Microsoft).

Tags: ,
  1. Tháng Mười Một 24, 2010 lúc 1:23 chiều

    Bài viết của bạn rất hấp dẫn!

  1. No trackbacks yet.

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s

%d bloggers like this: