2011-12-12 34 views
50

Trong hàng đợi mpmc giới hạn tuyệt vời của Dmitry Vyukov được viết bằng C++ Xem: http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queueLàm cách nào và khi nào để căn chỉnh kích thước đường bộ nhớ cache?

Ông ấy thêm một số biến số đệm. Tôi đoán điều này là để làm cho nó phù hợp với một dòng bộ nhớ cache cho hiệu suất.

Tôi có một số câu hỏi.

  1. Tại sao nó được thực hiện theo cách này?
  2. Đây có phải là phương pháp di động sẽ luôn hoạt động
  3. Thay vào đó, tốt nhất nên sử dụng __attribute__ ((aligned (64))).
  4. tại sao phải đệm trước khi con trỏ đệm trợ giúp hiệu suất? không chỉ là con trỏ được nạp vào bộ đệm nên nó thực sự chỉ là kích thước của một con trỏ?

    static size_t const  cacheline_size = 64; 
    typedef char   cacheline_pad_t [cacheline_size]; 
    
    cacheline_pad_t   pad0_; 
    cell_t* const   buffer_; 
    size_t const   buffer_mask_; 
    cacheline_pad_t   pad1_; 
    std::atomic<size_t>  enqueue_pos_; 
    cacheline_pad_t   pad2_; 
    std::atomic<size_t>  dequeue_pos_; 
    cacheline_pad_t   pad3_; 
    

Sẽ khái niệm này làm việc dưới gcc cho mã c?

Trả lời

34

Thực hiện theo cách này để các lõi khác nhau sửa đổi các trường khác nhau sẽ không phải trả lại dòng bộ nhớ cache chứa cả hai bộ nhớ cache giữa các bộ đệm của chúng. Nói chung, đối với một bộ xử lý để truy cập một số dữ liệu trong bộ nhớ, toàn bộ dòng bộ nhớ cache chứa nó phải nằm trong bộ nhớ cache cục bộ của bộ xử lý đó. Nếu nó sửa đổi dữ liệu đó, mục nhập bộ nhớ cache đó thường phải là bản sao duy nhất trong bất kỳ bộ nhớ cache nào trong hệ thống (Chế độ độc quyền trong giao thức kết hợp bộ nhớ cache MESI/MOESI). Khi các lõi riêng cố gắng sửa đổi các dữ liệu khác nhau xảy ra trên cùng một dòng bộ nhớ cache, và do đó lãng phí thời gian di chuyển toàn bộ đường đó qua lại, được gọi là chia sẻ sai.

Trong ví dụ cụ thể bạn đưa ra, một lõi có thể được enqueueing một mục (đọc (chia sẻ) buffer_ và viết (độc quyền) chỉ enqueue_pos_) trong khi người khác dequeues (chia sẻ buffer_ và độc quyền dequeue_pos_) mà không cần một trong hai lõi khựng lại trên một dòng bộ nhớ cache thuộc sở hữu của người khác.

Đệm ở đầu có nghĩa là buffer_buffer_mask_ kết thúc trên cùng một dòng bộ nhớ cache, thay vì chia thành hai dòng và do đó yêu cầu tăng gấp đôi lưu lượng bộ nhớ truy cập.

Tôi không chắc liệu kỹ thuật này có hoàn toàn di động hay không. Giả định rằng mỗi cacheline_pad_t sẽ tự nó được căn chỉnh với ranh giới bộ nhớ cache 64 byte (kích thước của nó), và do đó bất cứ điều gì theo sau nó sẽ nằm trên dòng bộ nhớ cache tiếp theo. Cho đến khi tôi biết, các tiêu chuẩn ngôn ngữ C và C++ chỉ yêu cầu cấu trúc toàn bộ này, để chúng có thể sống trong các mảng độc đáo, mà không vi phạm các yêu cầu liên kết của bất kỳ thành viên nào của chúng. (xem ý kiến)

Cách tiếp cận attribute sẽ cụ thể hơn, nhưng có thể cắt kích thước của cấu trúc này xuống một nửa, vì phần đệm sẽ bị giới hạn làm tròn mỗi phần tử thành một dòng bộ nhớ cache đầy đủ. Điều đó có thể khá có lợi nếu một người có rất nhiều thứ này.

Khái niệm tương tự áp dụng trong C cũng như C++.

+0

@Novelcrat - Ok điều đó có ý nghĩa rất nhiều. Vậy còn câu hỏi 2 và 3 thì sao? – Matt

+9

@MattH: Đối với tính di động C++ 11 giới thiệu 'std :: aligned_storage' cho phép bạn yêu cầu lưu trữ kích thước và căn chỉnh được xác định.Căn chỉnh mặc định cho 'char [N]' là '1' nếu không. –

+1

Tại sao trình liên kết không tối ưu hóa các biến padding nếu chúng không được sử dụng? – RishiD

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