programing

이미지 처리를 위한 매우 빠른 Memcpy?

lastmoon 2023. 8. 30. 21:57
반응형

이미지 처리를 위한 매우 빠른 Memcpy?

저는 C에서 메모리 주변의 큰 데이터 덩어리를 복사해야 하는 이미지 처리를 하고 있습니다. 소스와 대상이 겹치지 않습니다.

GCC를 사용하는 x86 플랫폼에서 이를 수행하는 가장 빠른 방법은 무엇입니까(SSE, SSE2는 사용할 수 있지만 SSE3는 사용할 수 없음)?

솔루션이 조립식이거나 GCC 내장형을 사용할 것으로 예상됩니다.

다음 링크를 찾았지만 이 링크가 가장 좋은 방법인지 알 수 없습니다(작성자도 버그가 몇 가지 있다고 말합니다).http://coding.derkeiler.com/Archive/Assembler/comp.lang.asm.x86/2006-02/msg00123.html

편집: 복사본이 필요합니다. 데이터를 복사해야 하는 번거로움을 피할 수 없습니다(이유는 설명할 수 있지만 설명은 생략하겠습니다:)).

William Chan과 Google 제공. Microsoft Visual Studio 2005의 memcpy보다 30-70% 더 빠릅니다.

void X_aligned_memcpy_sse2(void* dest, const void* src, const unsigned long size)
{

  __asm
  {
    mov esi, src;    //src pointer
    mov edi, dest;   //dest pointer

    mov ebx, size;   //ebx is our counter 
    shr ebx, 7;      //divide by 128 (8 * 128bit registers)


    loop_copy:
      prefetchnta 128[ESI]; //SSE2 prefetch
      prefetchnta 160[ESI];
      prefetchnta 192[ESI];
      prefetchnta 224[ESI];

      movdqa xmm0, 0[ESI]; //move data from src to registers
      movdqa xmm1, 16[ESI];
      movdqa xmm2, 32[ESI];
      movdqa xmm3, 48[ESI];
      movdqa xmm4, 64[ESI];
      movdqa xmm5, 80[ESI];
      movdqa xmm6, 96[ESI];
      movdqa xmm7, 112[ESI];

      movntdq 0[EDI], xmm0; //move data from registers to dest
      movntdq 16[EDI], xmm1;
      movntdq 32[EDI], xmm2;
      movntdq 48[EDI], xmm3;
      movntdq 64[EDI], xmm4;
      movntdq 80[EDI], xmm5;
      movntdq 96[EDI], xmm6;
      movntdq 112[EDI], xmm7;

      add esi, 128;
      add edi, 128;
      dec ebx;

      jnz loop_copy; //loop please
    loop_copy_end:
  }
}

정확한 상황과 가정에 따라 추가로 최적화할 수 있습니다.

또한 memcpy 소스(memcpy.asm)를 체크아웃하고 특수 케이스 처리를 제거할 수도 있습니다.추가 최적화가 가능할 수도 있습니다!

하팔리바시가 게시한 SSE-Code가 방법입니다.

더 많은 성능이 필요하고 장치 드라이버를 작성하는 길고 구불구불한 길을 망설이지 마십시오.오늘날 모든 중요한 플랫폼에는 복사 작업을 더 빨리 수행하고 CPU 코드와 병렬로 수행할 수 있는 DMA 컨트롤러가 있습니다.

하지만 그것은 운전기사를 쓰는 것을 포함합니다.보안 위험 때문에 이 기능을 사용자 측에 공개하는 빅 OS는 없는 것으로 알고 있습니다.

그러나 이러한 작업을 수행하도록 설계된 하드웨어를 능가하는 코드는 지구상에 없기 때문에 성능이 필요한 경우에는 그럴 만한 가치가 있을 수 있습니다.

이 질문은 이제 4년이 지났는데 아직 아무도 메모리 대역폭에 대해 언급하지 않았다는 것이 조금 놀랍습니다.CPU-Z가 내 컴퓨터에 PC3-10700 RAM이 있다고 보고합니다.RAM의 최대 대역폭(전송 속도, 처리량 등)은 10700MB/s입니다.제 기계의 CPU는 i5-2430M CPU이고, 최고 터보 주파수는 3GHz입니다.

이론적으로, 무한히 빠른 CPU와 RAM을 사용하면 memcpy는 5300MB/sec, 즉 memcpy가 RAM에서 읽고 쓰기 때문에 10700의 절반 수준으로 전송될 수 있습니다. (편집:v.odou가 지적했듯이, 이것은 단순한 근사치입니다.).

반면에, 무한히 빠른 RAM과 현실적인 CPU가 있다고 상상해 보십시오. 무엇을 달성할 수 있을까요?3GHz CPU를 예로 들어 보겠습니다.매 사이클마다 32비트 읽기와 32비트 쓰기를 수행할 수 있다면 3e9 * 4 = 12000 MBytes/sec를 전송할 수 있습니다.이것은 현대의 CPU가 쉽게 접근할 수 있는 것처럼 보입니다. 이미 CPU에서 실행되는 코드가 실제로 병목 현상이 아니라는 것을 알 수 있습니다.이것이 현대 컴퓨터에 데이터 캐시가 있는 이유 중 하나입니다.

데이터가 캐슁된 것을 알았을 때 memcpy를 벤치마킹하여 CPU가 실제로 무엇을 할 수 있는지 측정할 수 있습니다.이것을 정확하게 하는 것은 무모합니다.저는 임의의 숫자를 배열에 쓰고, 그것들을 다른 배열에 메모한 다음 복사된 데이터를 확인하는 간단한 앱을 만들었습니다.저는 영리한 컴파일러가 복사본을 제거하지 않았는지 확인하기 위해 디버거의 코드를 살펴 보았습니다.어레이의 크기를 변경하면 캐쉬 성능이 변경됩니다. 즉, 작은 어레이는 캐쉬에 적합하고 큰 어레이는 그렇지 않습니다.다음과 같은 결과를 얻었습니다.

  • 40KByte 어레이: 16000MB/초
  • 400KByte 어레이: 11,000MB/s
  • 4000KByte 어레이: 3100MB/sec

16000은 위에서 이론적으로 계산한 12000보다 많기 때문에 CPU는 사이클당 32비트 이상을 읽고 쓸 수 있습니다.이는 CPU가 이미 생각했던 것보다 병목 현상이 덜하다는 것을 의미합니다.저는 Visual Studio 2005를 사용했고, 표준 memcpy 구현에 들어가 보니 제 기계에서 movqda 명령어를 사용하는 것을 알 수 있었습니다.이것은 사이클당 64비트를 읽고 쓸 수 있다고 생각합니다.

게시된 멋진 코드 하팔리바시는 VS 2005 구현보다 약 40% 빠른 4200MB/s를 내 컴퓨터에서 달성합니다.프리페치 명령어를 사용하여 캐시 성능을 향상시키기 때문에 더 빠르다고 생각합니다.

요약하자면, CPU에서 실행되는 코드는 병목 현상이 아니며 코드를 조정하는 것은 작은 개선만 할 뿐입니다.

모든 최적화 수준에서-O1그 이상의 는 또그이상는, 는다같기내사정다용니합과 같은 합니다.memcpy-march 변수개변수매(()-march=pentium4사용자가 언급한 기능 집합에 대해) 최적의 아키텍처별 인라인 코드를 생성해야 합니다.

저는 그것을 벤치마킹해서 무엇이 나오는지 볼 것입니다.

Intel 프로세서에만 해당되는 경우 IPP의 이점을 누릴 수 있습니다.Nvidia GPU와 함께 실행될 것이라는 것을 안다면 CUDA를 사용할 수 있습니다. 두 경우 모두 memcpy()를 최적화하는 것보다 더 넓게 보는 것이 나을 수 있습니다. 이들은 알고리즘을 더 높은 수준으로 개선할 수 있는 기회를 제공합니다.그러나 둘 다 특정 하드웨어에 의존합니다.

Windows를 사용하는 경우 그래픽 처리를 위한 특정 GPU 최적화 루틴이 있는 DirectX API를 사용하십시오(얼마나 빠를 수 있습니까?).CPU가 로드되지 않았습니다.GPU가 Munch하는 동안 다른 작업을 수행합니다.)

OS에 구애받지 않으려면 OpenGL을 사용해 보십시오.

조립자를 만지작거리지 마십시오. 10년 이상 숙련된 라이브러리 제작 소프트웨어 엔지니어를 능가하는 데 실패할 가능성이 너무 높기 때문입니다.

오래된 질문이지만 지금까지 아무도 지적하지 않은 두 가지:

  1. 대부분의 컴파일러에는 자체 버전이 있습니다.memcpy이후로memcpy잘 정의되어 있고 C 표준의 일부이기 때문에 컴파일러는 시스템 라이브러리와 함께 제공되는 구현을 사용할 필요가 없으며 자체 라이브러리를 자유롭게 사용할 수 있습니다.질문에 "본질적인"이라고 언급되어 있듯이, 음, 사실 당신은 대부분의 시간을 글로 씁니다.memcpy당신의 코드에서, 당신은 사실 컴파일러 고유 함수를 사용하고 있습니다. 그것이 컴파일러가 실제 호출을 하는 대신 내부적으로 사용할 것이기 때문입니다.memcpy인라인 기능까지 제공하여 함수 호출 오버헤드를 제거할 수 있습니다.

  2. 대부분의.memcpy이미 알고 있는 구현은 SSE2와 같은 것을 사용할 수 있을 때 내부적으로 사용합니다. 적어도 좋은 구현은 사용합니다.Visual Studio 2005 중 하나는 그것을 사용하지 않았을 수도 있지만 GCC는 오랫동안 그것을 사용해 왔습니다.물론 사용하는 내용은 빌드 설정에 따라 다릅니다.코드가 실행될 모든 CPU에서 사용할 수 있는 명령만 사용하므로 아키텍처를 올바르게 설정해야 합니다(예:march그리고.mtune) 및 기타 플래그(예: 선택적 명령 집합에 대한 지원 활성화).이 모든 것은 컴파일러가 생성하는 코드에 영향을 미칩니다.memcpy최종 2진법으로

따라서 항상 그렇듯이 컴파일러나 시스템을 능가할 수 있다고 가정하지 마십시오(다른 것일 수 있음).memcpy다양한 CPU에 대한 구현도 가능), 벤치마크를 통해 입증할 수 있습니다.벤치마크가 당신의 손으로 쓴 코드가 실생활에서 더 빠르다는 것을 보여주지 않는 한, 컴파일러와 시스템이 새로운 CPU를 채택할 것이고 시스템은 미래에 당신의 코드를 자동으로 더 빨리 실행하게 하는 업데이트를 받을 수 있기 때문에, 그들에게 그것을 맡기는 것이 좋습니다.손으로 직접 작성한 코드를 다시 최적화해야 하므로 업데이트를 직접 발송하지 않는 한 속도가 더 빨라지지 않습니다.

DMA 엔진에 접근할 수 있다면 더 빠른 것은 없을 것입니다.

언급URL : https://stackoverflow.com/questions/1715224/very-fast-memcpy-for-image-processing

반응형