문제

다음과 같은 C++ 메서드 서명이 있습니다.

    static extern void ImageProcessing(
        [MarshalAs(UnmanagedType.LPArray)]ushort[] inImage,
        [MarshalAs(UnmanagedType.LPArray)]ushort[] outImage,
        int inYSize, int inXSize);

내부 및 외부의 타이밍 방법으로 함수를 래핑했습니다.내부적으로 이 기능은 0.24초에 실행됩니다.외부적으로는 이 기능이 2.8초, 즉 약 12배 느린 속도로 실행됩니다.무슨 일이야?마샬링으로 인해 속도가 그렇게 느려지나요?그렇다면 어떻게 해결할 수 있습니까?안전하지 않은 코드로 이동하여 포인터 등을 사용해야 합니까?나는 추가 시간 비용이 어디서 나오는지 좀 당황스럽습니다.

도움이 되었습니까?

해결책 3

슬프게도 대답은 도움이 되기는 하지만 이러한 제안보다 훨씬 더 평범합니다.기본적으로 나는 타이밍을 어떻게 맞추는지 망쳤습니다.

내가 사용한 타이밍 코드는 다음과 같습니다.

Ipp32s timer;
ippGetCpuFreqMhz(&timer);
Ipp64u globalStart = ippGetCpuClocks();
globalStart = ippGetCpuClocks() *2 - globalStart; //use this method to get rid of the overhead of getting clock ticks

      //do some stuff

Ipp64u globalEnd = ippGetCpuClocks(); 
globalEnd = ippGetCpuClocks() *2 - globalEnd;
std::cout << "total runtime: " << ((Ipp64f)globalEnd - (Ipp64f)globalStart)/((Ipp64f)timer *1000000.0f) << " seconds" << std::endl;

이 코드는 인텔 컴파일러에만 해당되며 매우 정확한 시간 측정을 제공하도록 설계되었습니다.불행하게도 이러한 극도의 정밀도는 실행당 약 2.5초의 비용을 의미합니다.타이밍 코드를 제거하면 해당 시간 제약이 제거되었습니다.

하지만 여전히 런타임 지연이 있는 것으로 보입니다. 코드는 해당 타이밍 코드가 켜진 상태에서 0.24초를 보고하고 이제 대략 0.35초의 타이밍을 보고합니다. 이는 약 50%의 속도 비용이 있음을 의미합니다.

코드를 다음과 같이 변경합니다.

  static extern void ImageProcessing(
     IntPtr inImage, //[MarshalAs(UnmanagedType.LPArray)]ushort[] inImage,
     IntPtr outImage, //[MarshalAs(UnmanagedType.LPArray)]ushort[] outImage,
     int inYSize, int inXSize);

다음과 같이 호출했습니다.

        unsafe {
            fixed (ushort* inImagePtr = theInputImage.DataArray){
                fixed (ushort* outImagePtr = theResult){
                    ImageProcessing((IntPtr)inImagePtr,//theInputImage.DataArray,
                        (IntPtr)outImagePtr,//theResult,
                        ysize,
                        xsize);
                }
            }
        }

실행 시간을 0.3초(3회 실행의 평균)로 줄입니다.여전히 내 취향에는 너무 느리지만 10배 속도 향상은 확실히 내 상사가 받아들일 수 있는 범위 내에 있습니다.

다른 팁

보세요 이 기사.Compact Framework에 중점을 두고 있지만 일반 원칙은 데스크탑에도 적용됩니다.분석 섹션의 관련 인용문은 다음과 같습니다.

관리되는 호출은 기본 메서드를 직접 호출하지 않습니다.대신 GC 선점 상태를 확인하기 위한 호출(GC가 보류 중이고 기다려야 하는지 확인하기 위해)과 같은 일부 오버헤드 루틴을 수행해야 하는 JITted 스텁 메서드를 호출합니다.일부 마샬링 코드가 스텁에 JIT될 수도 있습니다.이 모든 작업에는 시간이 걸립니다.

편집하다:또한 읽어 볼 가치가 있는 것은 JITted 코드 성능에 대한 이 블로그 기사 - 다시 말하지만, CF에 국한되지만 여전히 관련성이 있습니다.도 있습니다 호출 스택 깊이와 성능에 미치는 영향을 다루는 기사, 이것은 아마도 CF에만 해당될 것입니다(데스크톱에서는 테스트되지 않음).

두 배열 매개변수를 IntPtr로 전환해 보셨나요?PInvoke는 마샬링 서명의 모든 유형이 blittable일 때 가장 빠릅니다.이는 Pinvoke가 데이터를 주고받기 위해 단순한 memcpy로 내려간다는 것을 의미합니다.

우리 팀에서는 PInvoke 레이어를 관리하는 가장 효과적인 방법은 다음과 같습니다.

  1. Marshall로 설정된 모든 것이 블릿 가능하다는 보장
  2. 필요에 따라 IntPtr 클래스를 조작하여 배열과 같은 유형을 수동으로 마샬링하는 데 비용을 지불합니다.래퍼 메서드/클래스가 많기 때문에 이는 매우 사소한 일입니다.

"이것이 더 빠를 것입니다"라는 답변과 마찬가지로 이것이 자신의 코드 기반임을 프로파일링해야 합니다.우리는 몇 가지 방법을 고려하고 프로파일링한 후에야 이 솔루션에 도달했습니다.

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