2010-08-26 54 views
10

Tôi đã thử nghiệm hiệu suất của System.Threading.Parallel vs Threading và tôi ngạc nhiên khi thấy Parallel mất nhiều thời gian hơn để hoàn thành các tác vụ hơn là luồng. Tôi chắc chắn đó là do kiến ​​thức giới hạn của tôi về Song song, mà tôi mới bắt đầu đọc.C# Song song Vs. Hiệu suất mã luồng

Tôi nghĩ tôi sẽ chia sẻ một vài đoạn trích và nếu có ai có thể chỉ ra cho tôi mã paralle đang chạy chậm hơn so với mã luồng. Cũng cố gắng chạy so sánh tương tự để tìm số nguyên tố và tìm thấy mã song song kết thúc muộn hơn nhiều so với mã luồng.

public class ThreadFactory 
{ 
    int workersCount; 
    private List<Thread> threads = new List<Thread>(); 

    public ThreadFactory(int threadCount, int workCount, Action<int, int, string> action) 
    { 
     workersCount = threadCount; 

     int totalWorkLoad = workCount; 
     int workLoad = totalWorkLoad/workersCount; 
     int extraLoad = totalWorkLoad % workersCount; 

     for (int i = 0; i < workersCount; i++) 
     { 
      int min, max; 
      if (i < (workersCount - 1)) 
      { 
       min = (i * workLoad); 
       max = ((i * workLoad) + workLoad - 1); 
      } 
      else 
      { 
       min = (i * workLoad); 
       max = (i * workLoad) + (workLoad - 1 + extraLoad); 
      } 
      string name = "Working Thread#" + i; 

      Thread worker = new Thread(() => { action(min, max, name); }); 
      worker.Name = name; 
      threads.Add(worker); 
     } 
    } 

    public void StartWorking() 
    { 
     foreach (Thread thread in threads) 
     { 
      thread.Start(); 
     } 

     foreach (Thread thread in threads) 
     { 
      thread.Join(); 
     } 
    } 
} 

Đây là chương trình:

Stopwatch watch = new Stopwatch(); 
watch.Start(); 
int path = 1; 

List<int> numbers = new List<int>(Enumerable.Range(0, 10000)); 

if (path == 1) 
{ 
    Parallel.ForEach(numbers, x => 
    { 
     Console.WriteLine(x); 
     Thread.Sleep(1); 

    }); 
} 
else 
{ 
    ThreadFactory workers = new ThreadFactory(10, numbers.Count, (min, max, text) => { 

     for (int i = min; i <= max; i++) 
     { 
      Console.WriteLine(numbers[i]); 
      Thread.Sleep(1); 
     } 
    }); 

    workers.StartWorking(); 
} 

watch.Stop(); 
Console.WriteLine(watch.Elapsed.TotalSeconds.ToString()); 

Console.ReadLine(); 

Cập nhật:

Lấy Khóa cân nhắc: Tôi đã thử đoạn sau. Một lần nữa kết quả tương tự, song song dường như kết thúc chậm hơn nhiều.

đường dẫn = 1; cieling = 10000000;

List<int> numbers = new List<int>(); 

    if (path == 1) 
    { 
     Parallel.For(0, cieling, x => 
     { 
      lock (numbers) 
      { 
       numbers.Add(x);  
      } 

     }); 
    } 

    else 
    { 
     ThreadFactory workers = new ThreadFactory(10, cieling, (min, max, text) => 
     { 

      for (int i = min; i <= max; i++) 
      { 
       lock (numbers) 
       { 
        numbers.Add(i);  
       }      

      } 
     }); 

     workers.StartWorking(); 
    } 

Cập nhật 2: Chỉ cần một cập nhật nhanh chóng mà máy tính của tôi có Quad Core Processor. Vì vậy, song song có 4 lõi có sẵn.

+1

Bạn không nên khóa trên ForEach, nó thực hiện nội bộ này. Nhưng sử dụng ReaderWriterLockSlim, sẽ làm cho nó nhanh trở lại;) –

+0

Đặt ThreadFactory của bạn thành 2 luồng và đặt đồng thời tối đa trên Parallel.For thành 2, loại bỏ Console.WriteLine và làm điều gì đó phù hợp hơn. Bây giờ họ so sánh như thế nào? Hãy thử 3 và 3; 4 và 4; ... Tại một số điểm Parallel.ForEach sẽ quyết định nó phân bổ đủ chủ đề và sẽ phân bổ ít hơn tối đa bạn nói với nó, nhưng ít nhất là đến thời điểm đó bạn sẽ được so sánh timings bằng cách sử dụng * cùng * số chủ đề. –

+0

@Hightechrider: Vâng về ném khối lượng công việc thực tế vào nó, như tôi đã đề cập trong câu hỏi, tôi đã thử nghiệm điều này trước việc tìm kiếm số nguyên tố, xử lý khá chuyên sâu, cho thấy 100% hoạt động trong suốt và thấy rằng ThreadFactory chạy waaaaaayyy nhanh hơn. Hãy thử nó ra và xem .. Tôi thậm chí đã cố gắng thiết lập số lượng thread đến 2,3 vv Kết quả tương tự. – ace

Trả lời

3

đề cập đến một blog post bởi Reed Copsey Jr:

Parallel.ForEach là phức tạp hơn một chút, tuy nhiên. Khi làm việc với một IEnumerable chung, số lượng các mục cần thiết để xử lý không được biết trước và phải được phát hiện khi chạy. Ngoài ra, vì chúng tôi không có quyền truy cập trực tiếp vào từng phần tử, nên trình lên lịch phải liệt kê bộ sưu tập để xử lý nó. Vì IEnumerable không phải là luồng an toàn, nó phải khóa trên các phần tử khi nó liệt kê, tạo các bộ sưu tập tạm thời cho từng đoạn để xử lý và lập lịch này ra.

Việc khóa và sao chép có thể làm cho Parallel.ForEach mất nhiều thời gian hơn. Ngoài ra phân vùng và lịch trình của ForEach có thể tác động và cung cấp cho overhead. Tôi đã kiểm tra mã của bạn và tăng giấc ngủ của mỗi tác vụ, và sau đó kết quả là gần hơn, nhưng vẫn ForEach là chậm hơn.

[Edit - nghiên cứu thêm]

tôi thêm phần sau đây để thực hiện bẹ:

if (Thread.CurrentThread.ManagedThreadId > maxThreadId) 
    maxThreadId = Thread.CurrentThread.ManagedThreadId; 

Điều này hiển thị trên máy tính của tôi là nó sử dụng 10 bài ít hơn với ForEach, so với một trong những khác với các thiết lập hiện tại. Nếu bạn muốn nhiều chủ đề hơn từ ForEach, bạn sẽ phải fiddle xung quanh với ParallelOptions và Scheduler.

Xem Does Parallel.ForEach limits the number of active threads?

+0

Intresting ... hãy để tôi thử so sánh với chèn vào một danh sách với khóa được kích hoạt. Thnx. – ace

+0

Thấy cập nhật của bạn ... và sửa đổi câu trả lời của tôi một chút. –

+0

Đã chỉnh sửa một lần nữa :) Nó tóm tắt số chủ đề đang được sử dụng. –

3

Tôi nghĩ rằng tôi có thể trả lời câu hỏi của bạn. Trước hết, bạn không viết số lõi mà hệ thống của bạn có. nếu bạn đang chạy một lõi kép, chỉ có 4 thread sẽ làm việc bằng cách sử dụng Parallel.For trong khi bạn đang làm việc với 10 chủ đề trong ví dụ Thread của bạn.Thêm chủ đề sẽ làm việc tốt hơn như nhiệm vụ bạn đang chạy (In + Ngủ ngắn) là một nhiệm vụ rất ngắn cho luồng và chi phí chủ đề là rất lớn so với nhiệm vụ, tôi gần như chắc chắn rằng nếu bạn viết cùng một mã mà không có chủ đề nó sẽ hoạt động nhanh hơn.

Cả hai phương pháp của bạn hoạt động khá giống nhau nhưng nếu bạn tạo tất cả các chủ đề trước, bạn lưu allot như Parallel.For sử dụng nhóm tác vụ để thêm một số chi phí di chuyển.

+0

+1: câu hỏi là tổng số táo so với cam so sánh bởi vì nó sử dụng một số luồng khác nhau. Console.WriteLine cũng là một lựa chọn tồi cho một trường hợp thử nghiệm. –

+0

Tôi có một lõi tứ. @Hightechrider Tôi thậm chí đã thử nghiệm nó với việc tìm số nguyên tố. Cùng một kết quả.Vui lòng thử ví dụ mã của tôi và xem kết quả. – ace

+0

Nhắc nhở lần nữa - lời hứa duy nhất của Parallel-s là lập lịch "tốt hơn bằng tay". Nếu không có nó không sử dụng nhiều ngoại trừ một cú pháp thuận tiện có thể. – ZXX

0

So sánh không phải là rất công bằng đối với Threading.Parallel. Bạn nói với hồ bơi chủ đề tùy chỉnh của bạn rằng nó sẽ cần 10 chủ đề. Threading.Parallel không biết bao nhiêu chủ đề nó sẽ cần để nó cố gắng thích ứng với thời gian chạy có tính đến những thứ như tải CPU hiện tại và những thứ khác. Vì số lần lặp lại trong bài kiểm tra đủ nhỏ, bạn có thể số lượng chủ đề này được áp dụng. Cung cấp các gợi ý tương tự cho Threading.Parallel sẽ làm cho nó chạy nhanh hơn nhiều:

 

int workerThreads; 
int completionPortThreads; 
ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads); 
ThreadPool.SetMinThreads(10, completionPortThreads); 
 
+0

Thnx ... tôi sẽ thử điều đó và xem nó có tạo sự khác biệt không. – ace

+0

Đừng quên rằng lời hứa duy nhất của Parallel-s là lập kế hoạch "tốt hơn bằng tay". Điều tuyệt vời cho trường hợp null mặc dù - một muỗng hơn để nuôi trong :-) – ZXX

0

Đó là logic :-)

Đó sẽ là lần đầu tiên trong lịch sử mà bổ sung một (hoặc hai) lớp mã cải thiện hiệu suất. Khi bạn sử dụng thư viện tiện lợi, bạn sẽ phải trả giá. BTW bạn chưa đăng các số. Phải xuất bản kết quả :-)

Để làm cho mọi thứ thất bại hơn một chút (hoặc thiên vị :-) cho Parallel-s, hãy chuyển đổi danh sách thành mảng.

Sau đó, để làm cho chúng hoàn toàn không công bằng, hãy chia nhỏ công việc của riêng bạn, tạo một mảng chỉ 10 mục và thực hiện các hành động nạp thìa hoàn toàn cho Song song. Tất nhiên bạn đang thực hiện công việc mà Parallel-s hứa sẽ làm cho bạn vào thời điểm này nhưng nó nhất định là một số thú vị :-)

BTW Tôi chỉ đọc blog của Reed. Việc phân vùng được sử dụng trong câu hỏi này là những gì ông gọi là phân vùng đơn giản và ngây thơ nhất. Mà làm cho nó một thử nghiệm loại bỏ rất tốt thực sự. Bạn vẫn cần phải kiểm tra trường hợp làm việc bằng không chỉ để biết nếu nó hoàn toàn hosed.

+0

haha ​​... tốt nếu tôi đang làm tất cả các công việc sau đó nó đánh bại nghi thức điểm? – ace

+0

Nó cung cấp cho bạn các phép đo để cho bạn biết nếu song song có thể ít nhất là nhanh hơn khi họ không làm bất kỳ công việc - nghĩ về nó như một câu hỏi loại bỏ :-) Nó ít nhất là một phần hoạt động ra tốt sau đó họ có thể khả thi như một tính năng tiện lợi. Nếu không - bạn phát hiện sớm mà dll để tránh. – ZXX

+0

không chắc chắn nếu tôi đã sẵn sàng từ bỏ nó chưa, có nên được tối ưu hóa tốt hơn, cài đặt tôi đang thiếu trong khi thực hiện cuộc gọi song song của tôi. – ace

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