2011-01-12 38 views
73

Tôi đã thử nghiệm với đa luồng và xử lý song song và tôi cần một bộ đếm để thực hiện một số phép đếm cơ bản và thống kê về tốc độ xử lý. Để tránh các vấn đề với việc sử dụng đồng thời lớp học của tôi, tôi đã sử dụng một tuyên bố khóa trên một biến riêng tư trong lớp học của tôi:Khoản tiền khóa là bao nhiêu?

private object mutex = new object(); 

public void Count(int amount) 
{ 
lock(mutex) 
{ 
    done += amount; 
} 
} 

Nhưng tôi đã tự hỏi ... đắt bao nhiêu? Các tác động tiêu cực đến hiệu suất là gì?

+7

Việc khóa biến số không quá tốn kém; đó là sự chờ đợi trên một biến bị khóa mà bạn muốn tránh. – Gabe

+34

nó rẻ hơn rất nhiều so với chi tiêu giờ theo dõi xuống một điều kiện chủng tộc ;-) – BrokenGlass

+1

Vâng ... nếu một khóa là tốn kém, bạn có thể muốn tránh chúng bằng cách thay đổi chương trình để nó cần ít khóa hơn. Tôi có thể thực hiện một số loại đồng bộ hóa. –

Trả lời

63

Đây là an article được tính vào chi phí. Câu trả lời ngắn là 50ns.

+1

Vì vậy, trong kết luận các đối tượng nhiều hơn bạn có đắt hơn nó được. –

+13

Câu trả lời ngắn hơn: 50ns + thời gian chờ đợi nếu chủ đề khác đang giữ khóa. – Herman

+2

Các chủ đề khác đang được nhập và rời khỏi khóa, giá càng đắt. Chi phí mở rộng theo cấp số nhân với số lượng chủ đề –

17

Điều này không trả lời truy vấn của bạn về hiệu suất, nhưng tôi có thể nói .NET Framework cung cấp phương thức Interlocked.Add cho phép bạn thêm amount thành thành viên done mà không cần khóa thủ công trên đối tượng khác.

+1

Vâng, đây có lẽ là câu trả lời hay nhất. Nhưng chủ yếu vì lý do mã ngắn hơn và sạch hơn. Sự khác biệt về tốc độ không có khả năng đáng chú ý. –

+0

cảm ơn câu trả lời này. Tôi đang làm nhiều thứ hơn với ổ khóa. Ints được thêm vào là một trong nhiều. Yêu gợi ý, sẽ sử dụng nó từ bây giờ. –

+0

ổ khóa là nhiều, dễ dàng hơn nhiều để có được quyền, ngay cả khi mã khóa miễn phí có khả năng nhanh hơn. Interlocked.Add ngày của riêng mình có các vấn đề tương tự như + = không có đồng bộ hóa. – hangar

9

lock (Monitor.Enter/Exit) rất rẻ, rẻ hơn các lựa chọn thay thế như Waithandle hoặc Mutex.

Nhưng nếu nó chậm (một chút), bạn có muốn có một chương trình nhanh với kết quả không chính xác không?

+4

Haha ... Tôi đã đi cho chương trình nhanh và kết quả tốt. –

+0

@ henk-holterman Có nhiều vấn đề với tuyên bố của bạn: ** Đầu tiên ** như câu hỏi và câu trả lời này cho thấy rõ ràng, có sự hiểu biết thấp về tác động của khóa trên hiệu suất tổng thể, thậm chí mọi người nói về huyền thoại về 50ns với môi trường đơn luồng. ** Thứ hai ** tuyên bố của bạn là ở đây và sẽ ở lại trong nhiều năm và trong thời gian có nghĩa, bộ vi xử lý phát triển trong lõi, nhưng tốc độ của lõi không quá nhiều. ** Thrid ** ứng dụng trở nên phức tạp hơn theo thời gian, và sau đó nó là lớp khi lớp khóa trong môi trường của nhiều lõi và số lượng đang tăng lên, 2,4,8,10,20,16,32 – ipavlu

+0

Cách tiếp cận thông thường của tôi là xây dựng đồng bộ hóa theo cách lỏng lẻo cùng với ít tương tác nhất có thể. Điều đó rất nhanh với cấu trúc dữ liệu không khóa. Tôi đã làm cho các trình bao bọc mã của tôi xung quanh spinlock để đơn giản hóa việc phát triển và thậm chí khi TPL có các bộ sưu tập đồng thời đặc biệt, tôi đã phát triển các bộ sưu tập bị khóa xoay quanh danh sách, mảng, từ điển và hàng đợi của mình. spinlock. Tôi có thể nói với bạn, nó là có thể và cho phép để giải quyết nhiều kịch bản bộ sưu tập TPL không thể làm và với hiệu suất tuyệt vời/đạt được thông lượng. – ipavlu

4

Có một vài cách khác nhau để xác định "chi phí". Có chi phí thực tế của việc lấy và giải phóng khóa; như Jake viết, đó là không đáng kể trừ khi hoạt động này được thực hiện hàng triệu lần.

Mức độ liên quan hơn là hiệu ứng này có trên luồng thực thi. Mã này chỉ có thể được nhập bởi một luồng tại một thời điểm. Nếu bạn có 5 chủ đề thực hiện thao tác này một cách thường xuyên, 4 trong số đó sẽ kết thúc chờ khóa được giải phóng, và sau đó là chuỗi đầu tiên được lập lịch để nhập đoạn mã đó sau khi khóa đó được giải phóng. Vì vậy, thuật toán của bạn sẽ bị ảnh hưởng đáng kể. Bao nhiêu phụ thuộc vào thuật toán và tần suất hoạt động được gọi là .. Bạn thực sự không thể tránh nó mà không đưa ra các điều kiện chủng tộc, nhưng bạn có thể cải thiện nó bằng cách giảm thiểu số lượng cuộc gọi đến mã bị khóa.

43

Câu trả lời kỹ thuật là không thể định lượng được, nó phụ thuộc rất nhiều vào trạng thái bộ đệm ghi lại bộ nhớ CPU và lượng dữ liệu mà trình tìm nạp đã thu thập phải được loại bỏ và đọc lại. Đó là cả hai rất không xác định. Tôi sử dụng 150 chu kỳ CPU như là một xấp xỉ của phong bì ngược lại, tránh những thất vọng lớn.

Câu trả lời thực tế là nó là waaaay rẻ hơn khoảng thời gian bạn sẽ ghi khi gỡ lỗi mã khi bạn cho rằng bạn có thể bỏ qua khóa.

Để có được số khó, bạn sẽ phải đo lường. Visual Studio có một tiện ích mở rộng là concurrency analyzer.

+1

Trên thực tế không, nó có thể được định lượng và đo. Nó không phải là dễ dàng như viết những ổ khóa tất cả xung quanh mã, sau đó nói rằng nó là tất cả chỉ 50ns, một huyền thoại được đo trên truy cập luồng đơn để khóa. – ipavlu

+4

* "nghĩ rằng bạn có thể bỏ qua một khóa" * ... Tôi nghĩ rằng đó là nơi mà nhiều người đang ở khi họ đọc câu hỏi này ... – Snoopy

6

Chi phí cho một khóa trong một vòng lặp chặt chẽ, so với một thay thế không có khóa, là rất lớn. Bạn có thể đủ khả năng lặp lại nhiều lần và vẫn hiệu quả hơn khóa. Đó là lý do tại sao khóa hàng đợi miễn phí rất hiệu quả.

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace LockPerformanceConsoleApplication 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var stopwatch = new Stopwatch(); 
      const int LoopCount = (int) (100 * 1e6); 
      int counter = 0; 

      for (int repetition = 0; repetition < 5; repetition++) 
      { 
       stopwatch.Reset(); 
       stopwatch.Start(); 
       for (int i = 0; i < LoopCount; i++) 
        lock (stopwatch) 
         counter = i; 
       stopwatch.Stop(); 
       Console.WriteLine("With lock: {0}", stopwatch.ElapsedMilliseconds); 

       stopwatch.Reset(); 
       stopwatch.Start(); 
       for (int i = 0; i < LoopCount; i++) 
        counter = i; 
       stopwatch.Stop(); 
       Console.WriteLine("Without lock: {0}", stopwatch.ElapsedMilliseconds); 
      } 

      Console.ReadKey(); 
     } 
    } 
} 

Output:

With lock: 2013 
Without lock: 211 
With lock: 2002 
Without lock: 210 
With lock: 1989 
Without lock: 210 
With lock: 1987 
Without lock: 207 
With lock: 1988 
Without lock: 208 
+3

Đây có thể là một ví dụ xấu bởi vì vòng lặp của bạn thực sự không làm bất cứ điều gì, ngoài từ một phép gán biến duy nhất và một khóa là ít nhất 2 cuộc gọi hàm. Ngoài ra, 20ns mỗi khóa bạn đang nhận được không phải là xấu. –

22

Ôi trời ơi!

Có vẻ như câu trả lời đúng được gắn cờ ở đây vì TRẢ LỜI vốn đã không chính xác! Tôi muốn hỏi tác giả câu trả lời, trân trọng, để đọc bài báo liên kết đến cùng.article

Tác giả của bài báo từ năm 2003 article được đo trên chỉ máy Dual Core và trong trường hợp đo đầu tiên, ông đo khóa với chủ đề duy nhất chỉ và kết quả là khoảng 50ns mỗi truy cập khóa.

Nó không nói gì về khóa trong môi trường đồng thời. Vì vậy, chúng tôi phải tiếp tục đọc bài báo và trong nửa thứ hai tác giả đã đo kịch bản khóa với hai và ba chủ đề, mà được gần gũi hơn với mức độ đồng thời của bộ vi xử lý ngày nay.

Vì vậy, tác giả cho biết, với hai chủ đề trên Lõi kép, chi phí khóa là 120ns và với 3 chủ đề, nó được chuyển đến 180ns. Vì vậy, nó dường như rõ ràng phụ thuộc vào số lượng các chủ đề truy cập đồng thời và nhiều hơn nữa là tồi tệ hơn.

Vì vậy, nó là đơn giản, nó không phải là 50 ns, trừ khi nó là một sợi duy nhất, nơi khóa được vô ích.

Một vấn đề khác cần xem xét là nó được đo là thời gian trung bình!

Nếu thời gian lặp lại, sẽ có khoảng thời gian từ 1ms đến 20ms, đơn giản vì phần lớn là nhanh, nhưng vài luồng sẽ chờ xử lý thời gian và thậm chí phải mất vài mili giây.

Đây là tin xấu đối với bất kỳ loại ứng dụng nào yêu cầu thông lượng cao, độ trễ thấp.

Và vấn đề cuối cùng cần lưu ý là có thể có các hoạt động chậm hơn bên trong khóa và thường là trường hợp đó. Còn khối mã được thực hiện bên trong khóa, sự tranh chấp càng cao và sự chậm trễ tăng cao trên bầu trời. Hãy xem xét, hơn một thập kỷ đã trôi qua từ năm 2003, đó là vài thế hệ bộ vi xử lý được thiết kế đặc biệt để chạy đồng thời và khóa là làm hại đáng kể hiệu suất của chúng.

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