2012-01-13 2 views
4

나는std :: atomic 한 쌍의 원자 int32를 하나의 원자로 처리합니다. int64?

std::atomic<u32> _start; 
std::atomic<u32> _end; 

가 가끔 시작하거나 교환을 비교로 끝나는 설정할 부호 INT32의 쌍을, 그래서 전체 64 비트 쌍에 CAS를 사용하여 발생할 수 스퓨리어스 실패를 원하지 않는다. 32 비트 CAS를 사용하고 싶습니다.

_end.compare_exchange_strong(old_end, new_end); 

이제 원자 1 64 비트 읽기로 시작과 끝을 모두 가져올 수 있습니다. 또는 두 개의 별도 32 비트 읽기 두 개의 메모리 울타리가있는 두 개의 개별 32 비트 비트 읽기 (또는 컴파일러가이를 최적화하도록 하시겠습니까?)가 아닌, 하나의 64 비트 원자 페치 (적절한 메모리 펜스를 추가하는 컴파일러 사용)를 수행하는 것이 더 빠르지 않습니까?

어떻게 C++ 11에서 그렇게 할 수 있을까요?

+1

표준에서 유니온 허용 여부를 확인합니다. – PlasmaHH

+0

@PlasmaHH : 어떻게 도움이 될까요?데이터가 원래 저장되어 있던 멤버 외부의 공용체를 통해 멤버에 액세스하는 것은 정의되지 않은 동작입니다. C++에서는 많은 사용자가이 작업과 같은 특정 작업을 기대한다고해도 아무런 의미가 없다고 말하지 않습니다 . –

답변

2

표준에서는 std::atomics이 기본 유형과 동일한 크기가 아니거나 atomic의 연산이 lockfree가 아니라는 것을 보증하지 않습니다 (적어도 uint32 일 가능성이 있지만). 따라서 하나의 64bit 원자 적 연산으로 결합 할 수있는 적합한 방법이 없다는 것이 확실합니다. 따라서 두 변수를 수동으로 64 비트 연산에 결합할지 여부 (64 비트 연산에서만 작동 여부)를 결정해야합니다.

예를 들어, 플랫폼이 64bit CAS (첫 번째 Pentium IIRC로 추가 된 x86의 경우, 486 호환을 컴파일 할 때 사용할 수 없기 때문에)을 지원하지 않을 수 있습니다. 64bit 변수와 잠금 장치를 모두 포함

울타리에 관하여의 :. 우물은 memory_order 당신이 당신의 작업에 지정에 따라 메모리 순서가 두 작업은 그들이에 excuted 순서에 표시해야 함을 지정합니다. , 컴파일러 분명히 떨어져 울타리를 최적화 할 수 없을 것입니다 그렇지 않으면 수도 있습니다. 물론 당신이 x86을 대상으로 가정 memory_order_seq_cst 실제로 내가 무엇을 기억에서 barrierinstruction을 방출합니다, 그래서 모든 컴파일러가 수행 한 명령 재 배열을 방해하지는 않지만 실제 페널티는 발생하지 않을 것입니다). 플랫폼에 따라 물론

당신이하고 int64의 하나로서이 std::atomic<int32> 치료와 함께 멀리 얻을 수있는 캐스팅 중 하나 union 또는 reinterpret_cast를 통해, 바로이 동작이 표준에서 요구되지 않고 있음을 통보 할 수있는 (적어도 이론적으로) 언제든지 (새로운 컴파일러 verison, 다른 최적화 설정 등)

+2

'원자 '은 일부 플랫폼에서 중요한 섹션을 포함 할 수 있다고 생각합니다. 객체가 정확히 64 비트이거나 '원자 '의 두 배 크기라고 믿을 이유가 없습니다. –

+0

사실이지만 내게 관련이 없습니다. 원자가 근본적인 정수형보다 크다면 더 좋은 구현을 위해 도랑을 그립니다. 나는 x64에만 관심이있다. – Eloff

0

원자 적 업데이트가 필요하면 원자 64 비트 값으로 처리해야합니다. 다른 선택 사항은 없습니다. 별도의 정수 업데이트는 원 자성이 아니며 실행 가능하지 않습니다. 동의어는 여기에 관련이 없다는 데 동의하며, 대신 정수형 쌍을 (INT64)로 캐스팅하고 Cas64를 수행 할 것을 제안합니다.

임계 영역을 사용하면 과충전 - Cas64를 사용하면 약 33 머신 사이클 (비공개)으로 비용이 청구되는 반면, 임계 섹션은 100 사이클과 비슷한 비용이 더 많이 소요됩니다.

설명 된대로 Cas64를 사용하여 32 비트 포인터와 32 비트 포인터로 구성된 버전이 지정된 포인터에서 이와 똑같은 연산을 수행하는 것이 일반적입니다. 또한 캐시 라인 경계를 초과하는 값을 절대로 원하지 않기 때문에 실제로 "오른쪽 정렬"해야합니다.

관련 문제