원자 운영 비용


90

원자 적 작업의 비용은 얼마입니까 (비교 및 교체 또는 원자 적 추가 / 감소)? 얼마나 많은 사이클을 소비합니까? SMP 또는 NUMA에서 다른 프로세서를 일시 중지합니까, 아니면 메모리 액세스를 차단합니까? 순서가 잘못된 CPU에서 재정렬 버퍼를 플러시합니까?

캐시에는 어떤 영향이 있습니까?

저는 최신 인기 CPU 인 x86, x86_64, PowerPC, SPARC, Itanium에 관심이 있습니다.


@Jason S, Any. cas와 atomic inc / dec의 차이는 무시할 수 있습니다.
osgx

2
x86에서 원자 적 연산은 메모리 주소에 더 많은 경합이 발생함에 따라 느려집니다. 나는 일반적으로 비 잠금 작업보다 훨씬 느리다고 생각하지만 분명히 이것은 사용되는 작업, 경합 및 메모리 장벽에 따라 달라질 것입니다.
Stephen Nutt

흠. 쓰기는 x86에서 원자 적 인 것 같습니다. 'Understanding the Linux Kernel'-> spin_unlock
osgx 2010

32 비트 쓰기는 Java에서 원자 적입니다. LOCK 접두사를 추가하지 않는 한 1을 추가하는 것은 일반적으로 원 자성이 아닙니다. Linux 커널에 대해서는 spin_unlock을 볼 필요가 없습니다. 현재 릴리스에서는 arch / x86 / include / asm / atomic_32.h (이전에는 include / asm-i386 / atomic.h였습니다)를 참조하십시오.
Blaisorblade 2010

@Blaisorblade, JAva는 여기에 없습니다. 잠긴 작업의 비용은 얼마입니까?
osgx 2010

답변:


59

지난 며칠 동안 실제 데이터를 찾았지만 아무것도 찾지 못했습니다. 그러나 원자 작업 비용과 캐시 누락 비용을 비교하는 몇 가지 연구를 수행했습니다.

lock cmpxchgPentiumPro (문서에 설명 된대로) 이전 의 x86 LOCK 접두사 ( 원자 CAS 포함)의 비용은 메모리 액세스 (예 : 캐시 미스), + 다른 프로세서의 메모리 작업 중지, + 다른 프로세서와의 경합입니다. 버스를 잠그려고합니다. 그러나 PentiumPro는 일반 쓰기 저장 캐시 가능 메모리 (하드웨어와 직접 대화하지 않는 한 앱이 처리하는 모든 메모리)의 경우 모든 메모리 작업을 차단하는 대신 관련 캐시 라인 만 차단됩니다 ( @osgx의 답변 에있는 링크 기준 ). .

즉, 코어는 실제 locked 작업의 일부를 저장할 때까지 라인에 대한 MESI 공유 및 RFO 요청에 대한 응답을 지연 합니다. 이를 "캐시 잠금"이라고하며 한 캐시 라인에만 영향을줍니다. 다른 코어는 동시에 다른 라인을로드 / 저장하거나 CASing 할 수 있습니다.


실제로 CAS 케이스는 이 페이지에 설명 된대로 타이밍이 없지만 신뢰할 수있는 엔지니어의 통찰력있는 설명 으로 더 복잡 할 수 있습니다 . (적어도 실제 CAS 이전에 순수로드를 수행하는 일반적인 사용 사례의 경우)

너무 자세히 설명하기 전에 LOCKed 작업은 하나의 캐시 미스 + 동일한 캐시 라인에있는 다른 프로세서와의 경합 가능성이있는 반면, CAS + 이전로드 (항상 필요한 뮤텍스를 제외하고 거의 항상 필요함)를 말합니다. CAS 0 및 1)은 두 번의 캐시 미스 비용이 발생할 수 있습니다.

그는 단일 위치에서로드 + CAS를 사용하면 실제로로드 링크 / 스토어 조건부 (후자의 경우 참조)와 같이 캐시 미스 두 개가 발생할 수 있다고 설명합니다. 그의 설명은 MESI 캐시 일관성 프로토콜 에 대한 지식에 의존 합니다 . 캐시 라인에 대해 M (odified), E (xclusive), S (hared), I (nvalid) (따라서 MESI라고 함)의 4 가지 상태를 사용합니다. 설명 된 시나리오는 다음과 같습니다.

  • LOAD로 인해 캐시 미스가 발생합니다. 관련 캐시 라인이 공유 상태의 메모리에서로드됩니다 (즉, 다른 프로세서가 여전히 해당 캐시 라인을 메모리에 유지할 수 있으며이 상태에서는 변경이 허용되지 않음). 위치가 메모리에 있으면이 캐시 미스를 건너 뜁니다. 가능한 비용 : 1 회 캐시 미스. (캐시 라인이 Shared, Exclusive 또는 Modified 상태 인 경우 생략됩니다. 즉, 데이터가이 CPU의 L1 캐시에 있습니다.)
  • 프로그램은 저장할 새 값을 계산하고
  • 원자 CAS 명령어를 실행합니다.
    • 동시 수정을 피해야하므로 다른 CPU의 캐시에서 캐시 라인의 복사본을 제거하여 캐시 라인을 Exclusive 상태로 이동해야합니다. 가능한 비용 : 1 회 캐시 미스. 이미 독점적으로 소유 된 경우, 즉 Exclusive 또는 Modified 상태 인 경우에는 필요하지 않습니다. 두 상태 모두에서 다른 CPU는 캐시 라인을 보유하지 않지만 Exclusive 상태에서는 아직 수정되지 않았습니다.
    • 이 통신 후, 변수는 CPU의 로컬 캐시에서 수정되며,이 시점에서 다른 모든 CPU에서 전역 적으로 볼 수 있습니다 (캐시가 우리와 일관되기 때문). 결국 일반적인 알고리즘에 따라 주 메모리에 기록됩니다.
    • 해당 변수를 읽거나 수정하려는 다른 프로세서는 먼저 공유 또는 배타 모드에서 해당 캐시 라인을 가져와야하며이를 위해이 프로세서에 연결하여 업데이트 된 버전의 캐시 라인을받습니다. 대신 LOCKed 작업은 캐시 미스 비용 만 발생할 수 있습니다 (캐시 라인이 Exclusive 상태에서 직접 요청되기 때문입니다).

모든 경우에 캐시 라인 요청은 이미 데이터를 수정하는 다른 프로세서에 의해 지연 될 수 있습니다.


다른 CPU의 상태 변경 비용이 1 캐시 미스로 발생하는 이유는 무엇입니까?
osgx

1
CPU 외부의 통신이기 때문에 캐시에 액세스하는 것보다 느립니다. 캐시 미스는 어쨌든 다른 CPU에서 전달되어야합니다. 실제로 AMD Hypertransport (아주 오래전부터) 또는 Intel의 Intel QuickPath Interconnect와 같은 직접 상호 연결을 사용하는 경우 최신 Xeon 프로세서에서 다른 CPU와 대화하는 것이 메모리와 대화하는 것보다 더 빠를 수 있습니다. Nehalem을 기반으로합니다. 그렇지 않으면 다른 CPU와의 통신이 메모리 용과 동일한 FSB에서 발생합니다. 자세한 내용은 Wikipedia에서 HyperTransport 및 Front Side Bus를 검색하십시오.
Blaisorblade

와우, 그의 비용이 그렇게 비싸다고는 생각하지 못했습니다. 캐시 미스는 수천 사이클이 될 수 있습니다.
Lothar

2
정말? 내가 익숙한 수치는 캐시 미스의 경우 100 회, 컨텍스트 / 권한 전환 (syscall 포함)의 경우 수천 사이클입니다.
Blaisorblade

1
캐시 미스는 수천 사이클이 아닙니다! 일반적으로 300-350 CPU 사이클 인 약 100ns입니다 ....
user997112 2014-06-05

37

다음 설정으로 몇 가지 프로파일 링을 수행했습니다. 테스트 시스템 (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

감사! 테스트 코드를 게시하거나 Core2 / Core i3 / i5 / i7을 직접 테스트 할 수 있습니까? 테스트 설정에서 모든 코어가 초기화 되었습니까?
osgx 2013

소스 코드를 추가했습니다. 하나의 코어 만 초기화되었습니다. 다른 기계의 결과를보고 싶습니다.
Timo 2013

CLFLUSH는 전체 캐시의 WBINVD보다 캐시 라인을 플러시하는 훨씬 가벼운 방법입니다. WBINVD 는 명령 캐시도 플러시하여 추가 캐시 누락을 유발합니다.
피터 코르

공유 상태에서 캐시 라인이 핫한 경우를 테스트하는 것이 흥미로울 것입니다. 다른 스레드가 순수 부하로 읽도록함으로써 그렇게 할 수 있습니다.
Peter Cordes

4

버스 기반 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에있는 캐시 라인의 공유 복사본을 죽이거나 삭제 한 것입니다 /).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.