2011-07-13 22 views
6

Đề cập đến (hơi ngày) paper bởi Hans Boehm, trong "Hoạt động nguyên tử". Nó đề cập rằng mô hình bộ nhớ (được đề xuất tại thời điểm đó) sẽ không ngăn cản một trình biên dịch tối ưu hóa kết hợp một chuỗi các tải, hoặc lưu trữ, trên cùng một biến được kết hợp thành một tải duy nhất. Ví dụ mình là như sau (cập nhật đến cú pháp hiện tại hy vọng đúng):Kết hợp các cửa hàng/tải các biến nguyên tử liên tiếp

Với

atomic<int> v; 

while(v.load(memory_order_acquire)) { ... } 

thể được tối ưu hóa để:

int a = v.load(memory_order_acquire); 
while(a) { ... } 

Rõ ràng điều này sẽ là xấu, như ông nói. Bây giờ câu hỏi của tôi là, vì bài báo có một chút cũ, mô hình bộ nhớ C++ 0x hiện tại có ngăn chặn kiểu tối ưu hóa này, hay nó vẫn được cho phép về mặt kỹ thuật?

Việc đọc tiêu chuẩn của tôi dường như nghiêng về phía nó không được phép, nhưng việc sử dụng ngữ nghĩa "có được" làm cho nó ít rõ ràng hơn. Ví dụ nếu nó là "seq_cst", mô hình có vẻ đảm bảo rằng tải phải tham gia vào tổng số thứ tự trên truy cập và tải giá trị chỉ một lần do đó dường như vi phạm thứ tự (vì nó phá vỡ trình tự xảy ra trước mối quan hệ).

Để có được, tôi giải thích 29.3.2 có nghĩa là tối ưu hóa này không thể xảy ra, vì bất kỳ hoạt động "giải phóng" nào phải được quan sát bởi thao tác "có được". Chỉ làm một việc có vẻ như không hợp lệ.

Vì vậy, câu hỏi của tôi là liệu mô hình hiện tại (trong tiêu chuẩn đang chờ xử lý) có cho phép loại tối ưu hóa này không? Và nếu có, thì phần nào đặc biệt cấm nó? Nếu không, việc sử dụng nguyên tử volatile có giải quyết được vấn đề không?

Và để thưởng, nếu hoạt động tải có thứ tự "thoải mái" thì tối ưu hóa có được phép không?

Trả lời

2

Chuẩn C++ 0x cố gắng loại bỏ tối ưu hóa này.

Các từ có liên quan là từ 29.3p13:

Triển khai sẽ làm cho các cửa hàng nguyên tử hiển thị với tải nguyên tử trong một khoảng thời gian hợp lý.

Nếu luồng chỉ thực hiện tải chỉ bao giờ phát ra một lệnh tải thì điều này bị vi phạm, như thể nó không ghi lần đầu tiên, nó sẽ không bao giờ nhìn thấy nó. Việc đặt thứ tự bộ nhớ được sử dụng cho tải là như thế nào, điều này cũng giống nhau đối với cả hai số memory_order_seq_cstmemory_order_relaxed.

Tuy nhiên, tối ưu hóa sau phép, trừ khi có cái gì đó trong vòng lặp buộc một trật tự:

while(v.load(memory_order_acquire)) { 
    for(unsigned __temp=0;__temp<100;++__temp) { 
     // original loop body goes here 
    } 
} 

tức là trình biên dịch có thể tạo ra mã thực thi tải thực tế tùy tiện thường xuyên, với điều kiện nó vẫn thực hiện chúng. Điều này thậm chí được phép cho memory_order_seq_cst trừ khi có các hoạt động khác memory_order_seq_cst trong vòng lặp, vì điều này tương đương với việc chạy 100 lần lặp lại giữa bất kỳ truy cập bộ nhớ nào với các luồng khác.

Ngoài ra, việc sử dụng memory_order_acquire không có tác dụng bạn mô tả --- không bắt buộc phải xem các hoạt động phát hành (ngoài 29.3p13 được trích dẫn ở trên), chỉ cần làm xem hoạt động giải phóng sau đó nó áp đặt các ràng buộc hiển thị trên các truy cập khác.

+0

Về ngữ nghĩa thu được: Có, tôi đã hiểu sai bit đó - bản phát hành có thể nhìn thấy thiết lập mối quan hệ được sắp xếp, nhưng về mặt kỹ thuật không có gì đảm bảo tôi thấy nó (ngoại trừ trình biên dịch/CPU đó sẽ nhanh chóng mất tất cả thị trường chia sẻ). –

+0

Khoảng 29.3p13, tôi cũng đọc bit này và đã bị cám dỗ để nghĩ rằng nó làm cho đảm bảo này. Nhưng bởi vì nó sử dụng thuật ngữ * thời gian hợp lý * nó không có giá trị và chỉ có thể được đưa ra khỏi tiêu chuẩn. Đó là một thuật ngữ vô nghĩa có nghĩa là nó không thể dựa vào trong những tình huống có độ trễ thấp. Ví dụ, trong dự án hiện tại của tôi, nếu thời gian này là bất kỳ hơn 10s của nano giây không có giá trị nào trong hoạt động nguyên tử (nói so với một khóa). –

+0

Bạn có đồng ý sau đó, như tôi nghĩ n.m sẽ, mà làm cho nguyên tử này một 'dễ bay hơi' ngăn cản trình biên dịch từ làm tối ưu hóa bạn đã hiển thị? –

0

Ngay từ giấy rất bạn đang liên kết:

bay hơi đảm bảo rằng các số bên phải của hoạt động trí nhớ là thực hiện.

Tiêu chuẩn nói về cơ bản giống nhau:

Tiếp cận đối tượng dễ bay hơi được đánh giá theo đúng quy tắc của máy trừu tượng.

Điều này luôn xảy ra, vì trình biên dịch C đầu tiên của Dennis Ritchie tôi nghĩ. Nó phải được theo cách này bởi vì bộ nhớ ánh xạ I/O đăng ký sẽ không hoạt động khác. Để đọc hai ký tự từ bàn phím của bạn, bạn cần phải đọc thanh ghi ánh xạ bộ nhớ tương ứng hai lần. Nếu trình biên dịch có ý tưởng khác về số lần đọc nó phải thực hiện, điều đó sẽ quá tệ!

+1

Đúng, nhưng câu hỏi là * phải * chúng tôi đặt 'bay hơi' trên dữ liệu 'nguyên tử 'của mình để đảm bảo dữ liệu được đọc chính xác? Các yêu cầu đồng bộ liên thread dường như hàm ý chúng ta có thể không. Tuy nhiên, trên cùng một tay các nguyên tử * làm * có các dạng biến dạng đủ năng lực của chúng. –

+0

Tôi nghĩ nếu bạn muốn mã 'while (v.load (memory_order_acquire)) {}' để làm việc tốt nhất với một đảm bảo tuyệt đối, thì bạn phải sử dụng 'volatile'. Bạn không cần sử dụng 'volatile' trong các trường hợp khác, chẳng hạn như thực hiện mô hình khóa kép, vì có tổng số thứ tự trong chuỗi kiểm tra-lock-check và trình biên dịch không thể sắp xếp lại nó để kiểm tra khóa hay bất cứ thứ gì, thậm chí không bay hơi. Hoặc như vậy là sự hiểu biết của tôi anyway. –

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