std :: atomic <T> :: is_lock_free ()가 constexpr뿐만 아니라 정적이 아닌 이유는 무엇입니까?


9

std :: atomic :: is_lock_free ()가 constexpr뿐만 아니라 정적이지 않은지 누구든지 말해 줄 수 있습니까? 비 정적 및 / 또는 비 constprpr로 갖는 것은 의미가 없습니다.



3
나는 거기에 "정렬"을 던질 것입니다.
맥스 랭 호프

@MaxLanghof 모든 인스턴스가 동일한 방식으로 정렬되는 것은 아닙니까?
curiousguy

1
마이크, 아니, 나는 몰랐지만이 힌트에 감사한다. 정말 도움이됩니다. 그러나 is_lock_free ()와 is_always_lock_free 사이에 결정이있는 이유를 스스로에게 묻고 있습니다. 언어는 정렬되지 않은 액세스가 어쨌든 정의되지 않은 동작을 갖도록 정의하기 때문에 정렬되지 않은 원자 때문에 발생할 수 없습니다.
보니 타 몬테로

답변:


10

cppreference에 설명 된 대로 :

std :: atomic_flag를 제외한 모든 원자 유형은 잠금없는 원자 CPU 명령을 사용하는 대신 뮤텍스 또는 기타 잠금 작업을 사용하여 구현할 수 있습니다. 원자 유형은 때로는 잠금이없는 경우도 있습니다. 예를 들어, 지정된 아키텍처에서 정렬 된 메모리 액세스 만 자연스럽게 원자 성인 경우 동일한 유형의 정렬되지 않은 객체는 잠금을 사용해야합니다.

C ++ 표준은 잠금없는 원자 연산에도 주소가 없어야하며 공유 메모리를 사용하는 프로세스 간 통신에 적합하도록 권장하지만 필수는 아닙니다.

여러 사람들이 언급했듯이, std::is_always_lock_free당신이 정말로 찾고있는 것일 수 있습니다.


편집 : 명확히하기 위해 C ++ 객체 유형에는 인스턴스 주소를 2의 거듭 제곱 ( [basic.align])으로 제한하는 정렬 값이 있습니다. 이러한 정렬 값은 기본 유형에 대해 구현 정의되며 유형의 크기와 같을 필요는 없습니다. 하드웨어가 실제로 지원할 수있는 것보다 더 엄격 할 수도 있습니다.

예를 들어 x86 (대부분)은 정렬되지 않은 액세스를 지원합니다. 그러나 alignof(double) == sizeof(double) == 8정렬되지 않은 액세스에는 많은 단점 (속도, 캐싱, 원 자성)이 있기 때문에 x86 용 컴파일러는 대부분 있습니다. 그러나 예를 들어 #pragma pack(1) struct X { char a; double b; };또는 alignas(1) double x;당신이 "정렬되지 않은"을 가질 수 있습니다 double. 따라서 cppreference가 "정렬 된 메모리 액세스"에 대해 이야기 할 때, C ++ 유형을 사용하여 정렬 요구 사항 (UB)과 상반되는 방식으로 사용하지 않고 하드웨어에 대한 유형의 자연스러운 정렬과 관련하여 그렇게합니다.

자세한 정보는 다음과 같습니다 . x86에서 성공적으로 정렬되지 않은 액세스가 실제로 미치는 영향은 무엇입니까?

아래 @Peter Cordes 의 통찰력있는 의견을 확인하십시오 !


1
32 비트 x86은에서 ABI를 찾는 좋은 예입니다 alignof(double)==4. 그러나 std::atomic<double>여전히 alignof() = 8런타임에 정렬을 확인하는 대신에 있습니다. 원자를 언더 정렬하는 팩형 구조체를 사용하면 ABI가 중단되고 지원되지 않습니다. (32 비트 x86 용 GCC는 8 바이트 객체에 자연스럽게 정렬하는 것을 선호하지만, 구조체 패키징 규칙은이를 무시하고 alignof(T)i386 시스템 V 와 같은 경우에만 기반합니다 . G ++ atomic<int64_t>는 구조체 내부가 원자가 아닐 수 있는 버그 가있었습니다. GCC (C ++이 아닌 C의 경우)에는 여전히이 버그가 있습니다!
Peter Cordes

2
그러나 C ++ 20을 올바르게 구현 std::atomic_ref<double>하면 double완전히 정렬되지 않은 것을 거부 하거나 런타임에 정렬이 평범 double하고 int64_t자연스럽게 적합하지 않은 플랫폼에서 정렬을 검사합니다 . ( atomic_ref<T>일반으로 선언 된 객체에서 작동 하기 때문에 추가 정렬을 할 수있는 기회없이 T최소 정렬 만 alignof(T)가능합니다.)
Peter Cordes

2
현재 수정 된 libstdc ++ 버그의 경우 gcc.gnu.org/bugzilla/show_bug.cgi?id=62259 , a 를 포함하여 여전히 손상된 C 버그의 경우 gcc.gnu.org/bugzilla/show_bug.cgi?id=65146 을 참조하십시오 . current로 컴파일 할 때 찢어짐을 보여주는 순수한 ISO C11 테스트 케이스 . 어쨌든, 나의 점은 실제 컴파일러에 따라 정렬 원자 연산을 지원하지 않으며, 런타임 검사를하지 않는다 (? 아직), 그래서이다 또는 단지 비 자성으로 이어질 것입니다; 개체는 여전히 개체임을보고합니다 . _Atomic int64_tgcc -m32#pragma pack__attribute__((packed))lock_free
Peter Cordes

1
그러나 예,의 목적은 is_lock_free()하는 것입니다 수 있도록 구현은 현재 사람이 실제로 수행 방식과 다르게 작동; HW 지원 원자 명령어를 사용하거나 잠금을 사용하기 위해 실제 정렬을 기반으로 런타임 검사를합니다.
Peter Cordes

3

사용할 수 있습니다 std::is_always_lock_free

is_lock_free 실제 시스템에 따라 다르며 컴파일 타임에 확인할 수 없습니다.

관련 설명 :

원자 유형은 때로는 잠금이없는 경우도 있습니다. 예를 들어, 지정된 아키텍처에서 정렬 된 메모리 액세스 만 자연스럽게 원자 성인 경우 동일한 유형의 정렬되지 않은 객체는 잠금을 사용해야합니다.


1
std::numeric_limits<int>::max아키텍처에 따라 다르지만 정적이며 정적 constexpr입니다. 나는이 질문에 대해 아무것도 잘못이있는 것 같아요,하지만 난 추론의 첫 번째 부분 구입 해달라고
463,035,818 idclev을

1
어쨌든 정의되지 않은 동작을 갖도록 언어 정렬되지 않은 액세스를 정의하지 않아 런타임시 잠금없는 평가가 의미가 없습니까?
보니 타 몬테로

1
언어가 후자를 정의되지 않은 동작으로 정의하므로 정렬 된 액세스와 정렬되지 않은 액세스를 결정하는 것은 의미가 없습니다.
보니 타 몬테로

@BonitaMontero "C ++ 객체 정렬에서 정렬되지 않음"의미와 "하드웨어가 좋아하는 방식으로 정렬되지 않음"의미가 있습니다. 그것들은 반드시 동일하지는 않지만 실제로는 자주 있습니다. 당신이 보여주는 예제는 컴파일러가 명백하게 내장 된 가정을 가지고 있는데, 둘 동일하다는 것 is_lock_free입니다. 그것은 단지 그 컴파일러에서 의미가 없다는 것을 의미합니다 .
Max Langhof

1
정렬 요구 사항이있는 경우 원자가 올바르게 정렬 될 수 있습니다.
보니 타 몬테로

1

Windows-PC에 Visual Studio 2019를 설치했으며이 devenv에는 ARMv8 컴파일러도 있습니다. ARMv8은 정렬되지 않은 액세스를 허용하지만 비교 및 ​​스왑, 잠금 추가 등은 정렬되도록 요구됩니다. 또한 ldp또는 stp(32 비트 레지스터의로드 쌍 또는 저장소 쌍)을 사용하는 순수로드 / 순수 저장소 는 자연적으로 정렬 될 때 원 자성 만 보장됩니다.

그래서 임의의 원자 포인터에 대해 is_lock_free ()가 반환하는 것을 확인하는 작은 프로그램을 작성했습니다. 코드는 다음과 같습니다.

#include <atomic>
#include <cstddef>

using namespace std;

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

그리고 이것은 isLockFreeAtomic의 분해입니다

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

이것은 단지 returns true일명 1입니다.

이 구현은 alignof( atomic<int64_t> ) == 8모든 atomic<int64_t>것이 올바르게 정렬되도록 사용 하도록 선택합니다 . 따라서 모든로드 및 스토어에서 런타임 정렬 검사가 필요하지 않습니다.

(편집자 주 : 이것은 일반적입니다. 대부분의 실제 C ++ 구현은 이런 방식으로 작동합니다. 이것이 std::is_always_lock_free매우 유용한 이유 입니다 . 일반적으로 항상 유형이 사실이기 때문 is_lock_free()입니다.)


1
네, 대부분의 구현을 제공하기 위해 선택 atomic<uint64_t>하고 alignof() == 8그래서 그들은 런타임에 정렬을 체크 할 필요가 없습니다. 이 오래된 API는 그렇지 않은 옵션을 제공하지만 현재 HW에서는 정렬 (그렇지 않으면 UB, 예를 들어 비원 자성)을 요구하는 것이 훨씬 더 합리적입니다. int64_t4 바이트 만 정렬 할 수 있는 32 비트 코드에서도 atomic<int64_t>8 바이트가 필요합니다. 다른 답변에 대한 내 의견
Peter Cordes

다른 말로 : 경우 컴파일러 선택한다면 만들기 위해 alignof근본적인 형 하드웨어의 "좋은"정렬과 같은 가치를, 다음 is_lock_free 항상있을 것이다 true(그래서 것이다 is_always_lock_free). 여기서 컴파일러는 정확하게 이것을 수행합니다. 그러나 API는 존재하므로 다른 컴파일러는 다른 작업을 수행 할 수 있습니다.
Max Langhof

1
언어에서 정렬되지 않은 액세스에 정의되지 않은 동작이 있다고 말하면 모든 원자가 올바르게 정렬되어야합니다. 그 때문에 어떤 런타임 검사도 구현하지 않습니다.
Bonita Montero

@BonitaMontero 예, 그러나 alignof(std::atomic<double>) == 1하드웨어가 double4에 대한 잠금없는 원자 연산 만 보장 할 수 있더라도 (예 : C ++ 의미에서 "정렬되지 않은 액세스"가 없으므로 UB가 없음) 언어에는 아무것도 없습니다. 8 바이트 경계. 그러면 컴파일러는 정렬되지 않은 경우 잠금을 사용해야합니다 (그리고 is_lock_free객체 인스턴스의 메모리 위치에 따라 적절한 부울 값을에서 반환 ).
Max Langhof
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.