다음을 통해 공유


이중 썽킹(C++)

이중 unking은 관리되는 컨텍스트에서 함수 호출이 Visual C++ 관리 함수를 호출하고 프로그램 실행에서 관리되는 함수를 호출하기 위해 함수의 네이티브 진입점을 호출할 때 발생할 수 있는 성능 손실을 나타냅니다. 이 항목에서는 이중 펑킹이 발생하는 위치와 이를 방지하여 성능을 향상시키는 방법에 대해 설명합니다.

설명

기본적으로 /clr컴파일할 때 관리되는 함수의 정의로 인해 컴파일러가 관리되는 진입점 및 네이티브 진입점을 생성합니다. 이렇게 하면 네이티브 및 관리되는 호출 사이트에서 관리되는 함수를 호출할 수 있습니다. 그러나 네이티브 진입점이 있는 경우 함수에 대한 모든 호출의 진입점이 될 수 있습니다. 호출 함수가 관리되는 경우 네이티브 진입점은 관리되는 진입점을 호출합니다. 실제로 함수를 호출하려면 두 번의 호출이 필요합니다(따라서 이중 unking). 예를 들어 가상 함수는 항상 네이티브 진입점을 통해 호출됩니다.

한 가지 해결 방법은 컴파일러에 관리되는 함수에 대한 네이티브 진입점을 생성하지 않도록 지시하는 것입니다. 이 함수는 __clrcall 호출 규칙을 사용하여 관리되는 컨텍스트에서만 호출됩니다.

마찬가지로 관리되는 함수를 내보내는 경우(dllexport, dllimport) 네이티브 진입점이 생성되고 해당 함수를 가져오고 호출하는 모든 함수가 네이티브 진입점을 통해 호출됩니다. 이 상황에서 이중 펑크를 방지하려면 네이티브 내보내기/가져오기 의미 체계를 사용하지 마세요. 를 통해 #using 메타데이터를 참조하기만 하면 됩니다(#using 지시문 참조).

불필요한 이중 펑크를 줄이기 위해 컴파일러가 업데이트되었습니다. 예를 들어 서명에 관리되는 형식이 있는 함수(반환 형식 포함)는 암시적으로 로 __clrcall표시됩니다.

예: 이중 unking

설명

다음 샘플에서는 이중 펑킹을 보여 줍니다. 네이티브(/clr 제외)를 컴파일할 때 가상 함수를 main 호출하면 '의 복사 생성자에 대한 T호출과 소멸자에 대한 호출이 하나씩 생성됩니다. /clr 및 __clrcall.을 사용하여 가상 함수를 선언할 때도 비슷한 동작이 수행됩니다. 그러나 /clr컴파일된 경우 함수 호출은 복사 생성자에 대한 호출을 생성하지만 네이티브에서 관리되는 thunk로 인해 복사 생성자에 대한 또 다른 호출이 있습니다.

코드

// double_thunking.cpp
// compile with: /clr
#include <stdio.h>
struct T {
   T() {
      puts(__FUNCSIG__);
   }

   T(const T&) {
      puts(__FUNCSIG__);
   }

   ~T() {
      puts(__FUNCSIG__);
   }

   T& operator=(const T&) {
      puts(__FUNCSIG__);
      return *this;
   }
};

struct S {
   virtual void /* __clrcall */ f(T t) {};
} s;

int main() {
   S* pS = &s;
   T t;

   printf("calling struct S\n");
   pS->f(t);
   printf("after calling struct S\n");
}

샘플 출력

__thiscall T::T(void)
calling struct S
__thiscall T::T(const struct T &)
__thiscall T::T(const struct T &)
__thiscall T::~T(void)
__thiscall T::~T(void)
after calling struct S
__thiscall T::~T(void)

예: 이중 unking의 효과

설명

이전 샘플은 이중 렁크의 존재를 보여 주었다. 이 샘플에서는 해당 효과를 보여 있습니다. 루프는 for 가상 함수를 호출하고 프로그램은 실행 시간을 보고합니다. 프로그램이 /clr컴파일될 때 가장 느린 시간이 보고됩니다. /clr 없이 컴파일하거나 가상 함수가 .으로 선언된 경우 가장 빠른 시간이 보고됩니다__clrcall.

코드

// double_thunking_2.cpp
// compile with: /clr
#include <time.h>
#include <stdio.h>

#pragma unmanaged
struct T {
   T() {}
   T(const T&) {}
   ~T() {}
   T& operator=(const T&) { return *this; }
};

struct S {
   virtual void /* __clrcall */ f(T t) {};
} s;

int main() {
   S* pS = &s;
   T t;
   clock_t start, finish;
   double  duration;
   start = clock();

   for ( int i = 0 ; i < 1000000 ; i++ )
      pS->f(t);

   finish = clock();
   duration = (double)(finish - start) / (CLOCKS_PER_SEC);
   printf( "%2.1f seconds\n", duration );
   printf("after calling struct S\n");
}

샘플 출력

4.2 seconds
after calling struct S

참고 항목

혼합형(네이티브 및 관리) 어셈블리