문제

XP64가 탑재된 Core2 시스템(Dell T5400)이 있습니다.

우리는 32 비트 프로세스를 실행할 때 Memcpy의 성능이 1.2gbyte/s의 순서에 있음을 관찰합니다.그러나 64 비트 프로세스의 Memcpy는 약 2.2GBYTE/S (또는 인텔 컴파일러 CRT의 MEMCPY와 함께 2.4GBYTE/S)를 달성합니다.초기 반응은 64 비트 코드로 제공되는 더 넓은 레지스터로 인해 이것을 설명하는 것일 수 있지만, 우리는 우리 자신의 memcpy-like sse 어셈블리 코드 (32에 관계없이 128 비트 와이드로드 스토어를 사용해야한다는 것을 관찰합니다. /64 비트의 프로세스)는 달성하는 사본 대역폭에서 유사한 상한을 보여줍니다.

내 질문은 실제로이 차이점은 실제로 무엇입니까?32 비트 프로세스가 램에 들어가기 위해 여분의 와우 64 후프를 뛰어 넘어야합니까?tlbs 또는 prefetchers와 관련이 있습니까?무엇 ?

통찰력을 가져 주셔서 감사합니다.

또한 제기된 인텔 포럼.

도움이 되었습니까?

해결책

물론 디버거를 사용하여 기계어 코드를 한 단계씩 실행하여 memcpy의 가장 안쪽 루프 내에서 실행되는 실제 기계어 명령어를 살펴봐야 합니다.다른 것은 단지 추측일 뿐입니다.

내 질문은 아마도 32비트와 64비트 자체에는 아무 관련이 없다는 것입니다.내 생각에는 SSE 비임시 저장소를 사용하여 더 빠른 라이브러리 루틴이 작성된 것 같습니다.

내부 루프에 기존로드 스토어 명령어의 변형이 포함 된 경우 대상 메모리를 기계의 캐시에 읽고 수정하고 다시 작성해야합니다.해당 읽기는 완전히 불필요하므로(읽고 있는 비트는 즉시 덮어쓰기되므로) 캐시를 우회하는 "비일시적" 쓰기 명령을 사용하여 메모리 대역폭을 절반으로 절약할 수 있습니다.이렇게 하면 왕복이 아닌 단방향으로 메모리를 여행하여 대상 메모리가 작성됩니다.

저는 인텔 컴파일러의 CRT 라이브러리를 모르기 때문에 이것은 단지 추측일 뿐입니다.32비트 libCRT가 동일한 작업을 수행할 수 없는 특별한 이유는 없지만, 귀하가 인용한 속도 향상은 movdqa 명령을 movnt로 변환하는 것만으로도 예상할 수 있는 수준입니다.

memcpy는 계산을 수행하지 않기 때문에 항상 메모리를 얼마나 빨리 읽고 쓸 수 있는지에 따라 결정됩니다.

다른 팁

나는 다음이 그것을 설명할 수 있다고 생각합니다.

메모리에서 레지스터로 데이터를 복사하고 다시 메모리로 복사하려면 다음을 수행합니다.

mov eax, [address]
mov [address2], eax

이는 주소에서 주소2로 32비트(4바이트)를 이동합니다.64비트 모드에서는 64비트에서도 마찬가지입니다.

mov rax, [address]
mov [address2], rax

이는 64비트, 2바이트를 주소에서 주소2로 이동합니다.Intel 사양에 따르면 "mov" 자체는 64비트이든 32비트이든 상관없이 대기 시간이 0.5이고 처리량은 0.5입니다.지연 시간은 명령어가 파이프라인을 통과하는 데 걸리는 클록 주기 수이며 처리량은 동일한 명령어를 다시 수락하기 전에 CPU가 기다려야 하는 시간입니다.보시다시피, 클록 사이클당 2개의 mov를 수행할 수 있지만 두 mov 사이에 1/2 클록 사이클을 기다려야 하므로 효과적으로 클록 사이클당 1개의 mov만 수행할 수 있습니다(또는 여기서 잘못 해석한 것입니까?보다 여기 PDF 자세한 내용은).

물론 mov reg, mem 데이터가 1차 또는 2차 수준 캐시에 있는지, 캐시에 전혀 없고 메모리에서 가져와야 하는지에 따라 0.5주기보다 길어질 수 있습니다.그러나 위의 대기 시간은 이 사실을 무시하고(위에 링크된 PDF 상태와 같이) mov에 필요한 모든 데이터가 이미 존재한다고 가정합니다(그렇지 않으면 대기 시간은 데이터가 어디에 있든 가져오는 데 걸리는 시간만큼 늘어납니다). 지금 당장 - 이것은 여러 클럭 사이클일 수 있으며 실행 중인 명령과 완전히 독립적입니다. PDF는 482/C-30페이지에 나와 있습니다.

흥미로운 점은 mov가 32비트인지 64비트인지 여부는 아무런 역할을 하지 않는다는 것입니다.즉, 메모리 대역폭이 제한 요소가 되지 않는 한 64비트 mov는 32비트 mov와 동일하게 빠르며 64비트를 사용할 때 동일한 양의 데이터를 A에서 B로 이동하는 데 mov의 절반만 필요하므로 처리량은 (이론적으로) 두 배 더 높아야 합니다(그렇지 않다는 사실은 아마도 메모리가 무제한이 아니기 때문일 것입니다).

좋아요, 이제 더 큰 SSE 레지스터를 사용할 때 처리량이 더 빨라져야 한다고 생각하시나요?AFAIK xmm 레지스터는 256이 아니라 128비트 너비입니다. BTW(Wikipedia 참조).그러나 대기 시간과 처리량을 고려했습니까?이동하려는 데이터가 128비트로 정렬되어 있는지 여부입니다.그것에 따라 다음을 사용하여 이동하거나

movdqa xmm1, [address]
movdqa [address2], xmm1

또는 정렬되지 않은 경우

movdqu xmm1, [address]
movdqu [address2], xmm1

movdqa/movdqu의 대기 시간은 1이고 처리량은 1입니다.따라서 명령을 실행하는 데 두 배의 시간이 걸리고 명령 이후의 대기 시간은 일반 mov보다 두 배 더 깁니다.

그리고 우리가 고려하지 않은 또 다른 사실은 CPU가 실제로 명령을 마이크로 작업으로 분할하고 이를 병렬로 실행할 수 있다는 사실입니다.이제 정말 복잡해지기 시작합니다...나에게는 너무 복잡합니다.

어쨌든, 나는 경험을 통해 xmm 레지스터로/에서 데이터를 로드하는 것이 일반 레지스터로/에서 데이터를 로드하는 것보다 훨씬 느리다는 것을 알고 있으므로 xmm 레지스터를 사용하여 전송 속도를 높이려는 아이디어는 첫 번째 순간부터 실패했습니다.나는 실제로 SSE memmove가 일반 것보다 훨씬 느리지 않다는 사실에 놀랐습니다.

나는 마침내 이것의 바닥에 도달했습니다. (그리고 Die in Sente의 대답은 올바른 줄에 있었습니다. 감사합니다)

아래에서 dst와 src는 512MByte std::Vector입니다.저는 Intel 10.1.029 컴파일러와 CRT를 사용하고 있습니다.

64비트에서는 둘 다

memcpy(&dst[0],&src[0],dst.size())

그리고

memcpy(&dst[0],&src[0],N)

여기서 N은 이전에 선언되었습니다. const size_t N=512*(1<<20);부르다

__intel_fast_memcpy

그 중 대부분은 다음으로 구성됩니다:

  000000014004ED80  lea         rcx,[rcx+40h] 
  000000014004ED84  lea         rdx,[rdx+40h] 
  000000014004ED88  lea         r8,[r8-40h] 
  000000014004ED8C  prefetchnta [rdx+180h] 
  000000014004ED93  movdqu      xmm0,xmmword ptr [rdx-40h] 
  000000014004ED98  movdqu      xmm1,xmmword ptr [rdx-30h] 
  000000014004ED9D  cmp         r8,40h 
  000000014004EDA1  movntdq     xmmword ptr [rcx-40h],xmm0 
  000000014004EDA6  movntdq     xmmword ptr [rcx-30h],xmm1 
  000000014004EDAB  movdqu      xmm2,xmmword ptr [rdx-20h] 
  000000014004EDB0  movdqu      xmm3,xmmword ptr [rdx-10h] 
  000000014004EDB5  movntdq     xmmword ptr [rcx-20h],xmm2 
  000000014004EDBA  movntdq     xmmword ptr [rcx-10h],xmm3 
  000000014004EDBF  jge         000000014004ED80 

~2200 MByte/s의 속도로 실행됩니다.

하지만 32비트에서는

memcpy(&dst[0],&src[0],dst.size())

전화

__intel_fast_memcpy

그 중 대부분은

  004447A0  sub         ecx,80h 
  004447A6  movdqa      xmm0,xmmword ptr [esi] 
  004447AA  movdqa      xmm1,xmmword ptr [esi+10h] 
  004447AF  movdqa      xmmword ptr [edx],xmm0 
  004447B3  movdqa      xmmword ptr [edx+10h],xmm1 
  004447B8  movdqa      xmm2,xmmword ptr [esi+20h] 
  004447BD  movdqa      xmm3,xmmword ptr [esi+30h] 
  004447C2  movdqa      xmmword ptr [edx+20h],xmm2 
  004447C7  movdqa      xmmword ptr [edx+30h],xmm3 
  004447CC  movdqa      xmm4,xmmword ptr [esi+40h] 
  004447D1  movdqa      xmm5,xmmword ptr [esi+50h] 
  004447D6  movdqa      xmmword ptr [edx+40h],xmm4 
  004447DB  movdqa      xmmword ptr [edx+50h],xmm5 
  004447E0  movdqa      xmm6,xmmword ptr [esi+60h] 
  004447E5  movdqa      xmm7,xmmword ptr [esi+70h] 
  004447EA  add         esi,80h 
  004447F0  movdqa      xmmword ptr [edx+60h],xmm6 
  004447F5  movdqa      xmmword ptr [edx+70h],xmm7 
  004447FA  add         edx,80h 
  00444800  cmp         ecx,80h 
  00444806  jge         004447A0

~1350 MByte/s에서만 실행됩니다.

하지만

memcpy(&dst[0],&src[0],N)

여기서 N은 이전에 선언되었습니다. const size_t N=512*(1<<20); 32비트에서 직접 호출로 컴파일합니다.

__intel_VEC_memcpy

그 중 대부분은

  0043FF40  movdqa      xmm0,xmmword ptr [esi] 
  0043FF44  movdqa      xmm1,xmmword ptr [esi+10h] 
  0043FF49  movdqa      xmm2,xmmword ptr [esi+20h] 
  0043FF4E  movdqa      xmm3,xmmword ptr [esi+30h] 
  0043FF53  movntdq     xmmword ptr [edi],xmm0 
  0043FF57  movntdq     xmmword ptr [edi+10h],xmm1 
  0043FF5C  movntdq     xmmword ptr [edi+20h],xmm2 
  0043FF61  movntdq     xmmword ptr [edi+30h],xmm3 
  0043FF66  movdqa      xmm4,xmmword ptr [esi+40h] 
  0043FF6B  movdqa      xmm5,xmmword ptr [esi+50h] 
  0043FF70  movdqa      xmm6,xmmword ptr [esi+60h] 
  0043FF75  movdqa      xmm7,xmmword ptr [esi+70h] 
  0043FF7A  movntdq     xmmword ptr [edi+40h],xmm4 
  0043FF7F  movntdq     xmmword ptr [edi+50h],xmm5 
  0043FF84  movntdq     xmmword ptr [edi+60h],xmm6 
  0043FF89  movntdq     xmmword ptr [edi+70h],xmm7 
  0043FF8E  lea         esi,[esi+80h] 
  0043FF94  lea         edi,[edi+80h] 
  0043FF9A  dec         ecx  
  0043FF9B  jne         ___intel_VEC_memcpy+244h (43FF40h) 

~2100MByte/s의 속도로 실행됩니다(그리고 32비트가 어떻게든 대역폭에 제한을 두지 않는다는 것을 증명합니다).

나는 내 자신의 memcpy-like sse 코드가 32 비트 빌드에서 비슷한 ~ 1300 mbyte/한계로 고통 받고 있다는 주장을 철회합니다.이제 32 또는 64 비트에서> 2gbyte/s를 얻는 데 아무런 문제가 없습니다.트릭 (위의 결과 힌트)은 비 임차 ( "스트리밍") 상점을 사용하는 것입니다 (예 : _mm_stream_ps 본질적인).

32비트가 좀 이상한 것 같아요."dst.size()"Memcpy는 결국 더 빨리 전화하지 않습니다"movnt"버전 (Memcpy에 들어가면 가장 놀라운 금액이 있습니다. CPUID 확인 및 휴리스틱 논리 예를 들어 실제 데이터 근처에 가기 전에 캐시 크기 등을 복사 할 바이트 수를 비교하지만 적어도 관찰 된 동작을 이해합니다 (그리고 Syswow64 또는 H/W 관련이 아닙니다).

내 추측으로는 64비트 프로세스가 메모리 버스 사용을 최적화하는 프로세서의 기본 64비트 메모리 크기를 사용하고 있다는 것입니다.

긍정적인 피드백을 보내주셔서 감사합니다!내 생각에 난 할 수있어 부분적으로 여기서 무슨 일이 일어나는지 설명해주세요.

memcpy에 비임시 저장소를 사용하는 것은 확실히 금식입니다. 만약에 당신은 단지 memcpy 호출의 타이밍을 맞추는 것뿐입니다.

반면에 애플리케이션을 벤치마킹하는 경우 movdqa 저장소는 대상 메모리를 캐시에 남겨둔다는 이점이 있습니다.아니면 적어도 캐시에 맞는 부분입니다.

따라서 런타임 라이브러리를 설계 중이고 memcpy를 호출한 애플리케이션이 memcpy 호출 직후 대상 버퍼를 사용할 것이라고 가정할 수 있다면 movdqa 버전을 제공하는 것이 좋습니다.이는 movntdq 버전을 따르는 메모리에서 CPU로의 이동을 효과적으로 최적화하고 호출 이후의 모든 명령이 더 빠르게 실행됩니다.

그러나 반면에 대상 버퍼가 프로세서 캐시에 비해 큰 경우 해당 최적화는 작동하지 않으며 movntdq 버전은 더 빠른 애플리케이션 벤치마크를 제공합니다.

따라서 memcpy 아이디어에는 내부적으로 여러 버전이 있을 것입니다.대상 버퍼가 프로세서 캐시에 비해 작으면 movdqa를 사용하고, 그렇지 않으면 대상 버퍼가 프로세서 캐시에 비해 크면 movntdq를 사용합니다.32비트 라이브러리에서 이런 일이 일어나는 것 같습니다.

물론 이 중 어느 것도 32비트와 64비트의 차이와는 아무런 관련이 없습니다.

내 추측은 64비트 라이브러리가 아직 성숙하지 못했다는 것입니다.개발자는 아직 해당 버전의 라이브러리에서 두 루틴을 모두 제공하지 못했습니다.

내 앞에 참고 자료가 없기 때문에 타이밍/지시 사항에 대해 절대적으로 긍정적이지는 않지만 여전히 이론을 제시할 수는 있습니다.32비트 모드에서 메모리 이동을 수행하는 경우 매 클록 주기마다 단일 32비트 값을 이동하는 "rep movsd"와 같은 작업을 수행하게 됩니다.64비트 모드에서는 매 클럭 사이클마다 단일 64비트 이동을 수행하는 "rep movsq"를 수행할 수 있습니다.해당 명령은 32비트 코드에서는 사용할 수 없으므로 실행 속도를 절반으로 낮추려면 2 x rep movsd(조각당 1사이클)를 수행하게 됩니다.

모든 메모리 대역폭/정렬 문제 등을 무시하고 매우 단순화되었지만 이것이 모든 것이 시작되는 곳입니다...

다음은 64비트 아키텍처용으로 특별히 설계된 memcpy 루틴의 예입니다.

void uint8copy(void *dest, void *src, size_t n){
    uint64_t * ss = (uint64_t)src;
    uint64_t * dd = (uint64_t)dest;
    n = n * sizeof(uint8_t)/sizeof(uint64_t); 

    while(n--)
        *dd++ = *ss++;
}//end uint8copy()

전체 기사는 여기에 있습니다:http://www.godlikemouse.com/2008/03/04/optimizing-memcpy-routines/

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top