Có nên triển khai lồng vào nhau dựa trên So sánhExchange sử dụng SpinWait?


8

Dưới đây là việc thực hiện một phương pháp lồng vào nhau dựa trên Interlocked.CompareExchange.

Có nên cho mã này sử dụng SpinWaitspin trước khi nhắc lại không?

public static bool AddIfLessThan(ref int location, int value, int comparison)
{
    int currentValue;
    do
    {
        currentValue = location; // Read the current value
        if (currentValue >= comparison) return false; // If "less than comparison" is NOT satisfied, return false
    }
    // Set to currentValue+value, iff still on currentValue; reiterate if not assigned
    while (Interlocked.CompareExchange(ref location, currentValue + value, currentValue) != currentValue);
    return true; // Assigned, so return true
}

Tôi đã thấy SpinWaitđược sử dụng trong kịch bản này, nhưng lý thuyết của tôi là nó không cần thiết. Rốt cuộc, vòng lặp chỉ chứa một số ít các hướng dẫn và luôn có một tiến trình thực hiện.

Giả sử rằng hai luồng đang chạy đua để thực hiện phương thức này và luồng đầu tiên thành công ngay lập tức, trong khi luồng thứ hai ban đầu không có thay đổi và phải nhắc lại. Không có ứng cử viên nào khác, liệu luồng thứ hai có thể thất bại ở lần thử thứ hai không?

Nếu luồng thứ hai của ví dụ không thể thất bại trong lần thử thứ hai, thì chúng ta có thể đạt được gì với a SpinWait? Cạo sạch một vài chu kỳ trong trường hợp không chắc là một trăm luồng đang chạy đua để thực hiện phương pháp?


3
@MindSwipe OP không hỏi liệu có cần thiết sử dụng Interlocked hay không; thay vào đó, anh ta có nên sử dụng SpinWait thay vì chỉ thân vòng lặp trống không?
Matthew Watson

1
Có lẽ bạn chỉ nên sử dụng SpinOnceđể ngăn hệ điều hành một luồng có khả năng bị bỏ đói. Xem stackoverflow.com/questions/37799381/ khăn
Matthew Watson

1
@MindSwipe Điều kiện cuộc đua đã được xử lý chính xác nhờ vào việc sử dụng Interlocked. Để làm rõ, tôi chỉ quan tâm đến việc một cái SpinWaitcó ý nghĩa hay không , ví dụ như để lưu chu kỳ CPU một cách có ý nghĩa hoặc (cảm ơn @MatthewWatson!) Ngăn chặn một hệ điều hành đơn luồng bị bỏ đói.
Timo

1
@AloisKraus Tôi hiểu rằng trên lý thuyết, nhưng lý do của tôi là vòng lặp này thành công ở lần thử tiếp theo , bất kể chúng ta có chờ đợi hay không. Đó là lý do tại sao tôi hy vọng rằng điều đó SpinWaitthậm chí sẽ không làm giảm mức tiêu thụ năng lượng, vì cùng một số lần thử sẽ được thực hiện! (Với hai luồng, đó là một lần thử cho lần đầu tiên và hai lần cho luồng thứ hai.)
Timo

1
@Timo, tôi nghĩ bạn có thể hưởng lợi từ khóa spin trên các nền tảng mà Interlocked không được hỗ trợ bởi phần cứng mà phải được mô phỏng.
Nick

Câu trả lời:


2

Ý kiến ​​không chuyên gia của tôi là trong trường hợp cụ thể này, trong đó hai luồng thỉnh thoảng gọi AddIfLessThan, a SpinWaitlà không cần thiết. Nó có thể có ích trong trường hợp cả hai luồng đều gọi AddIfLessThantrong một vòng lặp chặt chẽ, để mỗi luồng có thể làm cho tiến trình không bị gián đoạn trong một số giây.

Trên thực tế, tôi đã thực hiện một thử nghiệm và đo hiệu suất của một luồng gọi AddIfLessThantrong một vòng lặp chặt chẽ so với hai luồng. Hai luồng cần nhiều hơn gần bốn lần để tạo ra cùng một số vòng (cộng dồn). Thêm một SpinWaitvào hỗn hợp làm cho hai luồng chỉ chậm hơn một chút so với luồng đơn.


1
SpinWait có ảnh hưởng gì với một luồng, ví dụ: có lý do gì để không sử dụng nó không?
Ian Ringrose

2
@IanRingrose nó làm cho nó chậm hơn 15%. Tôi khai báo một biến kiểu SpinWaitbên trong AddIfLessThanphương thức và mặc dù nó là kiểu giá trị và SpinOncephương thức của nó không bao giờ được gọi, nhưng nó vẫn thêm một số chi phí.
Theodor Zoulias

3
@TheodorZoulias: Bạn có thể tuyên bố trong cơ thể IL của mình để bỏ qua init của người dân địa phương sẽ mang lại cho bạn sự trở lại hoàn hảo. Bạn cần phải phát ra mã IL cho việc này vì chưa có hỗ trợ ngôn ngữ nào cho nó. Xem github.com/dotnet/csharplang/blob/master/proposeals/NH Khi bạn muốn quay một lần, bạn có thể gọi Reset trên đó và sau đó là SpinOnce.
Alois Kraus

1
Cảm ơn @TheodorZoulias! FYI, hai chủ đề chỉ là một ví dụ để minh họa quan điểm của tôi. Đây là một thư viện lớp và có thể được sử dụng khi người tiêu dùng thấy phù hợp. Điều tốt nhất chúng ta có thể làm là đưa ra các giả định về khả năng điều kiện cuộc đua với nhiều luồng. Dựa vào thời gian hoạt động mất ít thời gian, tôi cho rằng nhiều khả năng nhiều luồng sẽ thực hiện thao tác này chồng chéo với nhau.
Timo

1
Nếu bạn đang cập nhật một vòng lặp chặt chẽ, có lẽ bạn nên xem xét đặt giá trị riêng tư và cập nhật một lần ở cuối vòng lặp. Nếu giá trị được chia sẻ cần giữ thực tế nhiều hơn hoặc ít hơn và vòng lặp cực kỳ dài, hãy xem xét cập nhật giá trị như bạn nâng cao một thanh tiến trình, cứ sau X ms.
tò mò

2

Hai chủ đề không chỉ là một chủ đề để SpinWaitthảo luận. Nhưng mã này không cho chúng ta biết có bao nhiêu luồng thực sự có thể cạnh tranh tài nguyên và với số lượng luồng tương đối cao sử dụng SpinWaitcó thể trở nên có lợi. Đặc biệt với số lượng luồng cao hơn, hàng luồng ảo đang cố gắng lấy tài nguyên thành công, sẽ dài hơn và những luồng cuối cùng được phục vụ có cơ hội tốt để vượt quá lát cắt thời gian được phân bổ bởi bộ lập lịch mà lần lượt có thể dẫn đến mức tiêu thụ CPU cao hơn và có thể ảnh hưởng đến việc thực hiện các luồng theo lịch trình khác ngay cả với mức độ ưu tiên cao hơn. CácSpinWaitcó câu trả lời tốt cho tình huống này bằng cách đặt một số giới hạn trên của các vòng quay được phép sau đó chuyển đổi ngữ cảnh sẽ được thực hiện. Vì vậy, đó là một sự đánh đổi hợp lý giữa việc cần thiết phải thực hiện một cuộc gọi hệ thống đắt tiền để kích hoạt chuyển đổi ngữ cảnh và tiêu thụ CPU ở chế độ người dùng không được kiểm soát, có nguy cơ ảnh hưởng đến việc thực thi các luồng khác trong một số tình huống.


Hai chủ đề chỉ là một ví dụ để minh họa quan điểm của tôi. Đây là một thư viện lớp. Chúng ta sẽ có thể đối phó với bất kỳ kịch bản thực tế nào, tính đến khả năng của nó. Dựa vào thời gian hoạt động mất ít thời gian, tôi cho rằng nhiều khả năng nhiều luồng sẽ thực hiện thao tác này chồng chéo với nhau.
Timo

Bạn làm cho một điểm công bằng. Tôi đồng ý rằng, với rất nhiều chủ đề, một số chủ đề sẽ thất bại nhiều trước khi thành công. Tuy nhiên, một phần trong tôi nghĩ rằng hoạt động này rất nhỏ đến nỗi, nói rằng, thực hiện nó 100 lần vẫn là đậu phộng. Với suy nghĩ này, bạn có mong đợi nó sẽ làm SpinWaitquá mức hay không?
Timo

2
@Timo, câu trả lời duy nhất là đo lường, đặc biệt là xem xét rằng đây sẽ là một mã thư viện có thể xử lý các tình huống tải và phần cứng khác nhau mà tôi đoán. Để công bằng hơn và do đó cho thông lượng tốt hơn bằng cách sử dụng SpinWaitchiến lược tốt hơn (với chi phí tương đối nhỏ hàng chục micro giây) so với không sử dụng trừ khi bạn loại trừ trước khả năng xảy ra tình huống tải thấp, vì vậy hãy tìm điểm tải mà thông lượng có thể bị ảnh hưởng sẽ cho bạn một gợi ý cho dù bạn có cần hay không (lưu ý rằng nó cũng có thể phụ thuộc vào số lượng lõi).
Dmytro Mukalov

Với nhiều luồng không cần tài nguyên chung trong một thời gian dài thường xuyên đến mức người ta sẽ sử dụng lát cắt thời gian của nó, tôi sẽ cân nhắc tạo các bản sao riêng của tài nguyên (sẽ được đồng bộ hóa sau) hoặc thậm chí từ bỏ các luồng.
tò mò

@cquilguy Như đã nêu 4 bình luận trở lên, đây là một thư viện lớp. Tất nhiên mã tiêu thụ có thể sử dụng một cách tiếp cận trường hợp cụ thể. Tuy nhiên, điểm của thư viện lớp là phải kiên cường với các trường hợp sử dụng khác nhau. Đó là lý do tại sao câu hỏi vẫn còn trong tình huống SpinWaitgiá trị gia tăng. Cho đến nay, những cái đó dường như là (A) trường hợp lõi đơn và (B) có thể là trường hợp tranh chấp rất cao.
Timo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.