2017-04-21 37 views
23

Cho đến bây giờ tôi đã sử dụng std::queue trong dự án của mình. Tôi đo thời gian trung bình mà một hoạt động cụ thể trên hàng đợi này yêu cầu.Sử dụng hàng đợi Boost.Lockfree chậm hơn so với sử dụng mutexes

Thời gian được đo trên 2 máy: Máy ảo Ubuntu cục bộ của tôi và máy chủ từ xa. Sử dụng std::queue, mức trung bình gần như giống nhau trên cả hai máy: ~ 750 micro giây.

Sau đó, tôi "nâng cấp" std::queue thành boost::lockfree::spsc_queue, vì vậy tôi có thể loại bỏ các mutexes bảo vệ hàng đợi. Trên máy ảo cục bộ của tôi, tôi có thể thấy hiệu suất rất lớn, trung bình hiện tại là 200 micro giây. Tuy nhiên, trên máy từ xa, mức trung bình lên tới 800 micro giây, chậm hơn so với trước đây.

Trước tiên tôi nghĩ điều này có thể là do máy điều khiển từ xa có thể không hỗ trợ việc thực hiện lock-free:

Từ Boost.Lockfree page:

Không phải tất cả phần cứng hỗ trợ cùng một bộ hướng dẫn nguyên tử. Nếu nó không có sẵn trong phần cứng, nó có thể được mô phỏng trong phần mềm bằng cách sử dụng bảo vệ. Tuy nhiên điều này có nhược điểm rõ ràng là mất tài sản không có khóa.

Để tìm hiểu xem các hướng dẫn này có được hỗ trợ hay không, boost::lockfree::queue có phương pháp được gọi là bool is_lock_free(void) const;. Tuy nhiên, boost::lockfree::spsc_queue không có một chức năng như thế này, mà, đối với tôi, ngụ ý rằng nó không dựa vào phần cứng và đó là luôn luôn lockfree - trên bất kỳ máy nào.

Lý do mất hiệu suất là gì?


đang exmple (Nhà sản xuất/tiêu dùng)

// c++11 compiler and boost library required 

#include <iostream> 
#include <cstdlib> 
#include <chrono> 
#include <async> 
#include <thread> 
/* Using blocking queue: 
* #include <mutex> 
* #include <queue> 
*/ 
#include <boost/lockfree/spsc_queue.hpp> 


boost::lockfree::spsc_queue<int, boost::lockfree::capacity<1024>> queue; 

/* Using blocking queue: 
* std::queue<int> queue; 
* std::mutex mutex; 
*/ 

int main() 
{ 
    auto producer = std::async(std::launch::async, [queue /*,mutex*/]() 
    { 
     // Producing data in a random interval 
     while(true) 
     { 
      /* Using the blocking queue, the mutex must be locked here. 
      * mutex.lock(); 
      */ 

      // Push random int (0-9999) 
      queue.push(std::rand() % 10000); 

      /* Using the blocking queue, the mutex must be unlocked here. 
      * mutex.unlock(); 
      */ 

      // Sleep for random duration (0-999 microseconds) 
      std::this_thread::sleep_for(std::chrono::microseconds(rand() % 1000)); 
     } 
    } 

    auto consumer = std::async(std::launch::async, [queue /*,mutex*/]() 
    { 
     // Example operation on the queue. 
     // Checks if 1234 was generated by the producer, returns if found. 

     while(true) 
     { 
      /* Using the blocking queue, the mutex must be locked here. 
      * mutex.lock(); 
      */ 

      int value; 
      while(queue.pop(value) 
      { 
       if(value == 1234) 
        return; 
      } 

      /* Using the blocking queue, the mutex must be unlocked here. 
      * mutex.unlock(); 
      */ 

      // Sleep for 100 microseconds 
      std::this_thread::sleep_for(std::chrono::microseconds(100)); 
     } 
    } 

    consumer.get(); 
    std::cout << "1234 was generated!" << std::endl; 
    return 0; 
} 
+1

Vui lòng xem xét thêm [mcve] cho phép tạo lại các phép đo hiệu suất của bạn. Điều đó sẽ cho phép một câu trả lời thực tế hơn. – Zulan

+0

Với sự quan tâm cao trong câu hỏi này, nó thực sự là không may rằng khía cạnh cốt lõi của sự khác biệt hiệu suất trên hai hệ thống khác nhau không thể được trả lời. Tôi nghĩ rằng có nhiều tiềm năng hơn cho một câu trả lời thực tế cụ thể nếu câu hỏi được cải thiện. – Zulan

+0

@Zulan Tôi sẽ cố gắng thêm một ví dụ cụ thể sớm. – Bobface

Trả lời

52

Khóa thuật toán miễn phí thường hoạt động kém hơn các thuật toán khóa dựa trên. Đó là lý do chính khiến họ không được sử dụng gần như thường xuyên.

Vấn đề với thuật toán khóa miễn phí là chúng tối đa hóa tranh chấp bằng cách cho phép các chủ đề cạnh tranh tiếp tục tranh luận. Ổ khóa tránh tranh chấp bằng cách de-lập kế hoạch chủ đề cạnh tranh. Khóa các thuật toán miễn phí, thành phép tính gần đúng đầu tiên, chỉ nên được sử dụng khi không thể hủy lịch trình các chủ đề cạnh tranh. Điều đó hiếm khi áp dụng cho mã cấp ứng dụng.

Hãy để tôi cung cấp cho bạn một giả thuyết rất khắc nghiệt. Hãy tưởng tượng bốn luồng đang chạy trên một CPU lõi kép điển hình, hiện đại. Các chủ đề A1 và A2 đang thao tác sưu tập A. Chủ đề B1 và ​​B2 đang thao tác sưu tập B.

Trước tiên, hãy tưởng tượng bộ sưu tập sử dụng khóa. Điều đó có nghĩa là nếu các chuỗi A1 và A2 (hoặc B1 và ​​B2) cố gắng chạy cùng lúc, một trong số chúng sẽ bị khóa bởi khóa. Vì vậy, rất nhanh chóng, một sợi chỉ và một sợi B sẽ chạy. Những chủ đề này sẽ chạy rất nhanh và sẽ không tranh cãi. Bất kỳ chuỗi thời gian nào cố gắng tranh luận, chuỗi xung đột sẽ bị hủy theo lịch. Yay.

Bây giờ, hãy tưởng tượng bộ sưu tập không sử dụng khóa. Bây giờ, các chủ đề A1 và A2 có thể chạy cùng một lúc. Điều này sẽ gây tranh cãi liên tục. Dòng bộ nhớ cache cho bộ sưu tập sẽ ping-pong giữa hai lõi. Xe buýt liên lõi có thể bị bão hòa. Hiệu suất sẽ rất khủng khiếp.

Một lần nữa, điều này là rất phóng đại. Nhưng bạn hiểu ý rồi đấy. Bạn muốn tránh tranh chấp, không phải chịu đựng càng nhiều càng tốt.

Tuy nhiên, bây giờ chạy thử nghiệm suy nghĩ này một lần nữa trong đó A1 và A2 là các chuỗi duy nhất trên toàn bộ hệ thống. Bây giờ, bộ sưu tập khóa miễn phí có lẽ tốt hơn (mặc dù bạn có thể thấy rằng nó tốt hơn chỉ để có một sợi trong trường hợp đó!).

Hầu như mọi lập trình viên đều trải qua giai đoạn mà họ cho rằng khóa không tốt và tránh khóa làm cho mã nhanh hơn. Cuối cùng, họ nhận ra rằng đó là ganh đua làm cho mọi thứ chậm và khóa, sử dụng đúng cách, giảm thiểu tranh chấp.

+7

Câu trả lời rất hay, kinh điển. Tôi sẽ bounty này khi cửa sổ thời gian trôi qua. – sehe

+1

Hmm Tôi nghĩ rằng tôi hiện đang trong cùng giai đoạn mà tôi có xu hướng tránh khóa: p, có bất kỳ tài nguyên/cuốn sách nào khác mà tôi có thể đọc được khi các chủ đề đó được xúc động không? –

+0

@AbhinavGauniyal Tôi được hỏi rất nhiều, và tôi chưa tìm được. Tôi đã nói chuyện với nhiều người đã trải qua quá trình đau đớn tương tự mà tôi đã làm nhiều năm trước. –

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