Tại sao std :: nguyên tử <T> :: is_lock_free () không tĩnh cũng như constexpr?


9

Bất cứ ai có thể cho tôi biết liệu std :: atomic :: is_lock_free () không tĩnh cũng như constexpr? Có nó không tĩnh và / hoặc không phải là constexpr không có ý nghĩa đối với tôi.


3
Bạn có biết is_always_lock_freekhông?
Mike van Dyke

3
Tôi sẽ ném "căn chỉnh" ra khỏi đó.
Max Langhof

@MaxLanghof Bạn có nghĩa là không phải tất cả các trường hợp sẽ được sắp xếp theo cùng một cách?
tò mò

1
Mike, không, tôi không biết, nhưng cảm ơn vì gợi ý này; Nó thực sự hữu ích cho tôi. Nhưng tôi đang tự hỏi tại sao có một quyết định giữa is_lock_free () và is_always_lock_free. Không thể là do các nguyên tử không được phân bổ, vì những người khác đã đề xuất ở đây, vì ngôn ngữ xác định các truy cập không được phân bổ để có hành vi không xác định nào.
Bonita Montero

Câu trả lời:


10

Như đã giải thích về cppreference :

Tất cả các loại nguyên tử ngoại trừ std :: atomic_flag có thể được thực hiện bằng cách sử dụng mutexes hoặc các hoạt động khóa khác, thay vì sử dụng các hướng dẫn CPU nguyên tử không khóa. Các loại nguyên tử đôi khi cũng được phép không có khóa, ví dụ: nếu chỉ các truy cập bộ nhớ được căn chỉnh là nguyên tử tự nhiên trên một kiến ​​trúc nhất định, các đối tượng sai lệch cùng loại phải sử dụng khóa.

Tiêu chuẩn C ++ khuyến nghị (nhưng không yêu cầu) rằng các hoạt động nguyên tử không khóa cũng không có địa chỉ, nghĩa là phù hợp để liên lạc giữa các quá trình sử dụng bộ nhớ dùng chung.

Như được đề cập bởi nhiều người khác, std::is_always_lock_freecó thể là những gì bạn đang thực sự tìm kiếm.


Chỉnh sửa: Để làm rõ, các loại đối tượng C ++ có giá trị căn chỉnh giới hạn địa chỉ của các thể hiện của chúng chỉ ở bội số quyền hạn nhất định của hai ( [basic.align]). Các giá trị căn chỉnh này được xác định theo thực hiện cho các loại cơ bản và không cần bằng kích thước của loại. Họ cũng có thể nghiêm ngặt hơn những gì phần cứng thực sự có thể hỗ trợ.

Ví dụ: x86 (hầu hết) hỗ trợ truy cập không được phân bổ. Tuy nhiên, bạn sẽ tìm thấy hầu hết các trình biên dịch có alignof(double) == sizeof(double) == 8x86, vì các truy cập không được sắp xếp có một loạt các nhược điểm (tốc độ, bộ đệm, nguyên tử ...). Nhưng ví dụ #pragma pack(1) struct X { char a; double b; };hoặc alignas(1) double x;cho phép bạn có "không được phân bổ" double. Vì vậy, khi cppreference nói về "truy cập bộ nhớ được căn chỉnh", có lẽ nó làm như vậy về mặt căn chỉnh tự nhiên của loại cho phần cứng, không sử dụng loại C ++ theo cách trái ngược với yêu cầu căn chỉnh của nó (sẽ là UB).

Dưới đây là thông tin thêm: Hiệu ứng thực tế của các truy cập không được phân bổ thành công trên x86 là gì?

Vui lòng kiểm tra các bình luận sâu sắc của @Peter Cordes bên dưới!


1
32-bit x86 là một ví dụ điển hình về nơi bạn tìm thấy ABI alignof(double)==4. Nhưng std::atomic<double>vẫn có alignof() = 8thay vì kiểm tra căn chỉnh trong thời gian chạy. Sử dụng một cấu trúc đóng gói mà nguyên tử dưới căn chỉnh sẽ phá vỡ ABI và không được hỗ trợ. (GCC cho 32-bit x86 thích để cung cấp cho 8-byte đối tượng liên kết tự nhiên, nhưng quy tắc struct-đóng gói ghi đè lên đó và chỉ dựa trên alignof(T), ví dụ như trên i386 Hệ thống V. G ++ sử dụng để có một lỗi ở đâu atomic<int64_t>bên trong một cấu trúc có thể không phải nguyên tử bởi vì nó chỉ là giả định. GCC (đối với C không phải C ++) vẫn có lỗi này!)
Peter Cordes

2
Nhưng việc triển khai chính xác C ++ 20 std::atomic_ref<double>sẽ từ chối doublehoàn toàn liên kết dưới mức hoặc sẽ kiểm tra căn chỉnh trong thời gian chạy trên các nền tảng nơi nó hợp pháp doubleint64_tkhông được căn chỉnh tự nhiên. (Bởi vì atomic_ref<T>hoạt động trên một đối tượng được khai báo là đồng bằng Tvà chỉ có căn chỉnh tối thiểu alignof(T)mà không có cơ hội cho nó căn chỉnh thêm.)
Peter Cordes

2
Xem gcc.gnu.org/ormszilla/show_orms.cgi?id=62259 để biết lỗi libstdc ++ đã được sửa bây giờ và gcc.gnu.org/ormszilla/show_orms.cgi?id=65146 để biết lỗi C vẫn bị hỏng, bao gồm cả lỗi C testcase ISO C11 thuần túy cho thấy sự xé rách của một _Atomic int64_tkhi được biên dịch với dòng điện gcc -m32. Dù sao, quan điểm của tôi là các trình biên dịch thực sự không hỗ trợ các nguyên tử được liên kết dưới mức và không thực hiện kiểm tra thời gian chạy (chưa?), Vì vậy #pragma packhoặc __attribute__((packed))sẽ chỉ dẫn đến phi nguyên tử; các đối tượng vẫn sẽ báo cáo rằng chúng là lock_free.
Peter Cordes

1
Nhưng vâng, mục đích của is_lock_free()việc cho phép các triển khai hoạt động khác với cách thực hiện hiện tại; với kiểm tra thời gian chạy dựa trên căn chỉnh thực tế để sử dụng các hướng dẫn nguyên tử được hỗ trợ bởi CTNH hoặc sử dụng khóa.
Peter Cordes

3

Bạn có thể sử dụng std::is_always_lock_free

is_lock_free phụ thuộc vào hệ thống thực tế và không thể được xác định tại thời điểm biên dịch.

Giải thích có liên quan:

Các loại nguyên tử đôi khi cũng được phép không có khóa, ví dụ: nếu chỉ các truy cập bộ nhớ được căn chỉnh là nguyên tử tự nhiên trên một kiến ​​trúc nhất định, các đối tượng sai lệch cùng loại phải sử dụng khóa.


1
std::numeric_limits<int>::maxphụ thuộc vào kiến ​​trúc, nhưng là tĩnh và constexpr. Tôi đoán không có gì sai trong câu trả lời, nhưng tôi không mua phần đầu tiên của lý luận
idclev 463035818

1
Không xác định ngôn ngữ truy cập không được phân bổ để có hành vi không xác định nào để việc đánh giá không có khóa hoặc không có thời gian chạy sẽ vô nghĩa?
Bonita Montero

1
Sẽ không có ý nghĩa khi quyết định giữa các truy cập được căn chỉnh và không được phân bổ vì ngôn ngữ định nghĩa hành vi sau là hành vi không xác định.
Bonita Montero

@BonitaMontero Có ý nghĩa "không được sắp xếp theo nghĩa đối tượng C ++" và "không được sắp xếp theo ý nghĩa của phần cứng". Những cái đó không nhất thiết giống nhau, nhưng trong thực tế chúng thường xuyên như vậy. Ví dụ bạn hiển thị là một ví dụ như vậy trong đó trình biên dịch dường như có giả định tích hợp rằng hai cái này giống nhau - điều đó chỉ có nghĩa is_lock_freelà vô nghĩa trên trình biên dịch đó .
Max Langhof

1
Bạn có thể khá chắc chắn rằng một nguyên tử sẽ có sự liên kết thích hợp nếu có một yêu cầu căn chỉnh.
Bonita Montero

1

Tôi đã cài đặt Visual Studio 2019 trên Windows-PC của mình và devenv này cũng có trình biên dịch ARMv8. ARMv8 cho phép truy cập không được phân bổ, nhưng so sánh và hoán đổi, bổ sung bị khóa, v.v ... bắt buộc phải được căn chỉnh. Và cả tải thuần / cửa hàng thuần túy sử dụng ldphoặc stp(cặp tải hoặc cặp thanh ghi 32 bit) chỉ được đảm bảo là nguyên tử khi chúng được căn chỉnh tự nhiên.

Vì vậy, tôi đã viết một chương trình nhỏ để kiểm tra những gì is_lock_free () trả về cho một con trỏ nguyên tử tùy ý. Vì vậy, đây là mã:

#include <atomic>
#include <cstddef>

using namespace std;

bool isLockFreeAtomic( atomic<uint64_t> *a64 )
{
    return a64->is_lock_free();
}

Và đây là sự phân tách của isLockFreeAtomic

|?isLockFreeAtomic@@YA_NPAU?$atomic@_K@std@@@Z| PROC
    movs        r0,#1
    bx          lr
ENDP

Đây chỉ là returns true, aka 1.

Việc thực hiện này chọn sử dụng alignof( atomic<int64_t> ) == 8để mọi thứ atomic<int64_t>được căn chỉnh chính xác. Điều này tránh sự cần thiết phải kiểm tra căn chỉnh thời gian chạy trên mỗi tải và lưu trữ.

(Lưu ý của biên tập viên: điều này là phổ biến; hầu hết các triển khai C ++ ngoài đời thực đều hoạt động theo cách này. Đây là lý do tại sao std::is_always_lock_freerất hữu ích: bởi vì nó thường đúng với các loại is_lock_free()đã từng đúng.)


1
Có, hầu hết các triển khai chọn để cho atomic<uint64_t>alignof() == 8 vì vậy họ không phải kiểm tra căn chỉnh khi chạy. API cũ này cung cấp cho họ tùy chọn không làm như vậy, nhưng trên CTNH hiện tại, điều đó có ý nghĩa hơn nhiều chỉ cần yêu cầu căn chỉnh (nếu không thì UB, ví dụ như không nguyên tử). Ngay cả trong mã 32 bit, nơi int64_tcó thể chỉ có căn chỉnh 4 byte, atomic<int64_t>yêu cầu 8 byte. Xem ý kiến ​​của tôi về câu trả lời khác
Peter Cordes

Đặt các từ khác nhau: Nếu trình biên dịch chọn tạo alignofgiá trị cho loại cơ bản giống như căn chỉnh "tốt" của phần cứng, thì is_lock_free sẽ luôn luôn true(và như vậy is_always_lock_free). Trình biên dịch của bạn ở đây thực hiện chính xác điều này. Nhưng API tồn tại để các trình biên dịch khác có thể làm những việc khác nhau.
Max Langhof

1
Bạn có thể khá chắc chắn rằng nếu ngôn ngữ nói rằng truy cập không được phân bổ có hành vi không xác định thì tất cả các nguyên tử phải được căn chỉnh chính xác. Không có triển khai sẽ thực hiện bất kỳ kiểm tra thời gian chạy vì điều đó.
Bonita

@BonitaMontero Có, nhưng trong ngôn ngữ không có gì cấm alignof(std::atomic<double>) == 1(vì vậy sẽ không có "quyền truy cập không được phân bổ" theo nghĩa C ++, do đó không có UB), ngay cả khi phần cứng chỉ có thể đảm bảo các hoạt động nguyên tử không khóadouble s trên 4 hoặc Ranh giới 8 byte. Trình biên dịch sau đó sẽ phải sử dụng các khóa trong các trường hợp không được sắp xếp (và trả về giá trị boolean thích hợp từ is_lock_free, tùy thuộc vào vị trí bộ nhớ của thể hiện đối tượng).
Max Langhof
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.