원자 적 작업의 비용은 얼마입니까 (비교 및 교체 또는 원자 적 추가 / 감소)? 얼마나 많은 사이클을 소비합니까? SMP 또는 NUMA에서 다른 프로세서를 일시 중지합니까, 아니면 메모리 액세스를 차단합니까? 순서가 잘못된 CPU에서 재정렬 버퍼를 플러시합니까?
캐시에는 어떤 영향이 있습니까?
저는 최신 인기 CPU 인 x86, x86_64, PowerPC, SPARC, Itanium에 관심이 있습니다.
원자 적 작업의 비용은 얼마입니까 (비교 및 교체 또는 원자 적 추가 / 감소)? 얼마나 많은 사이클을 소비합니까? SMP 또는 NUMA에서 다른 프로세서를 일시 중지합니까, 아니면 메모리 액세스를 차단합니까? 순서가 잘못된 CPU에서 재정렬 버퍼를 플러시합니까?
캐시에는 어떤 영향이 있습니까?
저는 최신 인기 CPU 인 x86, x86_64, PowerPC, SPARC, Itanium에 관심이 있습니다.
답변:
지난 며칠 동안 실제 데이터를 찾았지만 아무것도 찾지 못했습니다. 그러나 원자 작업 비용과 캐시 누락 비용을 비교하는 몇 가지 연구를 수행했습니다.
lock cmpxchg
PentiumPro (문서에 설명 된대로) 이전 의 x86 LOCK 접두사 ( 원자 CAS 포함)의 비용은 메모리 액세스 (예 : 캐시 미스), + 다른 프로세서의 메모리 작업 중지, + 다른 프로세서와의 경합입니다. 버스를 잠그려고합니다. 그러나 PentiumPro는 일반 쓰기 저장 캐시 가능 메모리 (하드웨어와 직접 대화하지 않는 한 앱이 처리하는 모든 메모리)의 경우 모든 메모리 작업을 차단하는 대신 관련 캐시 라인 만 차단됩니다 ( @osgx의 답변 에있는 링크 기준 ). .
즉, 코어는 실제 lock
ed 작업의 일부를 저장할 때까지 라인에 대한 MESI 공유 및 RFO 요청에 대한 응답을 지연 합니다. 이를 "캐시 잠금"이라고하며 한 캐시 라인에만 영향을줍니다. 다른 코어는 동시에 다른 라인을로드 / 저장하거나 CASing 할 수 있습니다.
실제로 CAS 케이스는 이 페이지에 설명 된대로 타이밍이 없지만 신뢰할 수있는 엔지니어의 통찰력있는 설명 으로 더 복잡 할 수 있습니다 . (적어도 실제 CAS 이전에 순수로드를 수행하는 일반적인 사용 사례의 경우)
너무 자세히 설명하기 전에 LOCKed 작업은 하나의 캐시 미스 + 동일한 캐시 라인에있는 다른 프로세서와의 경합 가능성이있는 반면, CAS + 이전로드 (항상 필요한 뮤텍스를 제외하고 거의 항상 필요함)를 말합니다. CAS 0 및 1)은 두 번의 캐시 미스 비용이 발생할 수 있습니다.
그는 단일 위치에서로드 + CAS를 사용하면 실제로로드 링크 / 스토어 조건부 (후자의 경우 참조)와 같이 캐시 미스 두 개가 발생할 수 있다고 설명합니다. 그의 설명은 MESI 캐시 일관성 프로토콜 에 대한 지식에 의존 합니다 . 캐시 라인에 대해 M (odified), E (xclusive), S (hared), I (nvalid) (따라서 MESI라고 함)의 4 가지 상태를 사용합니다. 설명 된 시나리오는 다음과 같습니다.
모든 경우에 캐시 라인 요청은 이미 데이터를 수정하는 다른 프로세서에 의해 지연 될 수 있습니다.
다음 설정으로 몇 가지 프로파일 링을 수행했습니다. 테스트 시스템 (AMD Athlon64 x2 3800+)이 부팅되고 긴 모드 (인터럽트 비활성화 됨)로 전환되고 관심 명령이 루프에서 실행되고 100 번의 반복이 풀리고 1,000 번의 루프 사이클이 실행되었습니다. 루프 본문은 16 바이트로 정렬되었습니다. 시간은 루프 전후에 rdtsc 명령으로 측정되었습니다. 또한 명령이없는 더미 루프가 실행되었으며 (루프 반복 당 2 사이클, 나머지는 14 사이클 측정) 명령 프로파일 링 시간의 결과에서 결과를 뺍니다.
다음 지침이 측정되었습니다.
lock cmpxchg [rsp - 8], rdx
"(비교 일치 및 불일치 모두 포함),lock xadd [rsp - 8], rdx
",lock bts qword ptr [rsp - 8], 1
"모든 경우에 측정 된 시간은 약 310 사이클이고 오류는 약 +/- 8 사이클이었습니다.
동일한 (캐시 된) 메모리에서 반복 실행에 대한 값입니다. 캐시 미스가 추가되면 시간이 상당히 길어집니다. 또한 이것은 2 개의 코어 중 하나만 활성화 된 상태에서 수행되었으므로 캐시가 독점적으로 소유되었으며 캐시 동기화가 필요하지 않았습니다.
캐시 미스에서 잠긴 명령어의 비용을 평가 wbinvld
하기 위해 잠긴 명령어 앞에 명령어를 추가하고 wbinvld
더하기 add [rsp - 8], rax
를 비교 루프에 넣었습니다 . 두 경우 모두 비용은 명령어 쌍당 약 80,000 사이클이었습니다! 잠금 bts의 경우 시간 차이는 명령 당 약 180 사이클이었습니다.
이것은 상호 처리량이지만 잠긴 작업은 작업을 직렬화하므로 지연 시간에 차이가 없을 수 있습니다.
결론 : 잠긴 작업은 무겁지만 캐시 미스는 훨씬 더 심할 수 있습니다. 또한 잠긴 작업은 캐시 누락을 일으키지 않습니다. 캐시 라인이 독점적으로 소유되지 않은 경우에만 캐시 동기화 트래픽이 발생할 수 있습니다.
컴퓨터를 부팅하기 위해 ReactOS 프로젝트에서 x64 버전의 FreeLdr을 사용했습니다. 다음은 asm 소스 코드입니다.
#define LOOP_COUNT 1000
#define UNROLLED_COUNT 100
PUBLIC ProfileDummy
ProfileDummy:
cli
// Get current TSC value into r8
rdtsc
mov r8, rdx
shl r8, 32
or r8, rax
mov rcx, LOOP_COUNT
jmp looper1
.align 16
looper1:
REPEAT UNROLLED_COUNT
// nothing, or add something to compare against
ENDR
dec rcx
jnz looper1
// Put new TSC minus old TSC into rax
rdtsc
shl rdx, 32
or rax, rdx
sub rax, r8
ret
PUBLIC ProfileFunction
ProfileFunction:
cli
rdtsc
mov r8, rdx
shl r8, 32
or r8, rax
mov rcx, LOOP_COUNT
jmp looper2
.align 16
looper2:
REPEAT UNROLLED_COUNT
// Put here the code you want to profile
// make sure it doesn't mess up non-volatiles or r8
lock bts qword ptr [rsp - 8], 1
ENDR
dec rcx
jnz looper2
rdtsc
shl rdx, 32
or rax, rdx
sub rax, r8
ret
버스 기반 SMP에서 원자 접두사 LOCK
는 버스 와이어 신호를 어설 션 (켜짐)합니다 LOCK#
. 버스에서 다른 CPU / 장치를 사용하는 것을 금지합니다.
Ppro 및 P2 책 http://books.google.com/books?id=3gDmyIYvFH4C&pg=PA245&dq=lock+instruction+pentium&lr=&ei=_E61S5ehLI78zQSzrqwI&cd=1#v=onepage&q=lock%20instruction%20pentium&f=false 페이지 244-246
잠긴 명령어는 직렬화 중이며 작업을 동기화하고 있습니다 .... / about 비 순차 / 잠금 RMW / read-modify-write = 원자 자체 / 명령어는 프로세서가 잠긴 명령어를 실행하기 전에 모든 명령어를 실행하도록합니다. / about yet not flushed writes / 다음 명령어를 실행하기 전에 프로세서 내의 모든 게시 된 쓰기가 외부 메모리로 플러시되도록합니다.
/ about SMP / 세마포가 S 상태의 캐시에 있습니다 ... 0 바이트의 날짜에 대해 읽기 및 무효화 트랜잭션을 발행합니다 (이는 인접 CPU에있는 캐시 라인의 공유 복사본을 죽이거나 삭제 한 것입니다 /).