共用方式為


Double Thunking (C++)

雙指指當 Managed 內容中的函式呼叫呼叫 Visual C++ Managed 函式,以及程式執行呼叫函式的原生進入點以呼叫 Managed 函式時,您可能會遇到的效能遺失。 本主題討論雙指的發生位置,以及如何避免其改善效能。

備註

根據預設,使用 /clr 進行編譯時,Managed 函式的定義會導致編譯程式產生Managed進入點和原生進入點。 這可讓Managed函式從原生和受控呼叫月臺呼叫。 不過,當原生進入點存在時,它可以是函式所有呼叫的進入點。 如果受管理呼叫函式,原生進入點會接著呼叫Managed進入點。 實際上,需要兩個呼叫才能叫用函式(因此,雙指)。 例如,虛擬函式一律會透過原生進入點呼叫。

其中一個解決方法是告訴編譯程式不要產生 Managed 函式的原生進入點,該函式只會使用 __clrcall 呼叫慣例,從 Managed 內容呼叫。

同樣地,如果您導出Managed函式(dllexport、dllimport),就會產生原生進入點,而且任何匯入和呼叫該函式的函式都會透過原生進入點呼叫。 為了避免在此情況下發生雙指,請勿使用原生匯出/匯入語意;只要透過 #using 參考元數據(請參閱 #using 指示詞)。

編譯程式已更新,以減少不必要的雙指。 例如,簽章中具有Managed型別的任何函式(包括傳回型別)都會隱含地標示為 __clrcall

範例:雙指

描述

下列範例示範雙指。 編譯原生時(不含 /clr),中的 main 虛擬函式呼叫會產生對 複製建構函式的一個呼叫 T,以及對解構函式的一次呼叫。 當虛擬函式以 /clr__clrcall宣告時,就會達到類似的行為。 不過,當剛使用 /clr 編譯時,函式呼叫會產生對複製建構函式的呼叫,但因為原生到 Managed 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)

範例:雙指效果

描述

上一個範例示範雙指的存在。 此範例會顯示其效果。 迴圈會 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

另請參閱

混合 (原生和 Managed) 組件