Bagikan melalui


Double Thunking (C++)

Penghentian ganda mengacu pada hilangnya performa yang dapat Anda alami ketika panggilan fungsi dalam konteks terkelola memanggil fungsi terkelola Visual C++ dan di mana eksekusi program memanggil titik masuk asli fungsi untuk memanggil fungsi terkelola. Topik ini membahas di mana pengintaian ganda terjadi dan bagaimana Anda dapat menghindarinya untuk meningkatkan performa.

Keterangan

Secara default, saat mengkompilasi dengan /clr, definisi fungsi terkelola menyebabkan pengkompilasi menghasilkan titik masuk terkelola dan titik masuk asli. Ini memungkinkan fungsi terkelola dipanggil dari situs panggilan asli dan terkelola. Namun, ketika titik masuk asli ada, itu bisa menjadi titik masuk untuk semua panggilan ke fungsi. Jika fungsi panggilan dikelola, titik masuk asli kemudian akan memanggil titik masuk terkelola. Akibatnya, dua panggilan diperlukan untuk memanggil fungsi (karenanya, mengintai ganda). Misalnya, fungsi virtual selalu dipanggil melalui titik masuk asli.

Salah satu resolusinya adalah memberi tahu pengkompilasi untuk tidak menghasilkan titik masuk asli untuk fungsi terkelola, bahwa fungsi hanya akan dipanggil dari konteks terkelola, dengan menggunakan konvensi panggilan __clrcall .

Demikian pula, jika Anda mengekspor (dllexport, dllimport) fungsi terkelola, titik masuk asli dihasilkan dan fungsi apa pun yang mengimpor dan memanggil fungsi tersebut akan memanggil melalui titik masuk asli. Untuk menghindari pengintaian ganda dalam situasi ini, jangan gunakan semantik ekspor/impor asli; cukup referensikan metadata melalui #using (lihat Direktif #using).

Kompiler telah diperbarui untuk mengurangi pengintaian ganda yang tidak perlu. Misalnya, fungsi apa pun dengan jenis terkelola dalam tanda tangan (termasuk jenis pengembalian) secara implisit akan ditandai sebagai __clrcall.

Contoh: Double thunking

Deskripsi

Sampel berikut menunjukkan pengintaian ganda. Ketika dikompilasi asli (tanpa /clr), panggilan ke fungsi virtual dalam main menghasilkan satu panggilan ke Tkonstruktor salinan dan satu panggilan ke destruktor. Perilaku serupa dicapai ketika fungsi virtual dideklarasikan dengan /clr dan __clrcall. Namun, ketika baru saja dikompilasi dengan /clr, panggilan fungsi menghasilkan panggilan ke konstruktor salinan tetapi ada panggilan lain ke konstruktor salinan karena thunk asli-ke-dikelola.

Kode

// 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");
}

Output sampel

__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)

Contoh: Efek penghentian ganda

Deskripsi

Sampel sebelumnya menunjukkan adanya pengintaian ganda. Sampel ini menunjukkan efeknya. Perulangan for memanggil fungsi virtual dan program melaporkan waktu eksekusi. Waktu paling lambat dilaporkan ketika program dikompilasi dengan /clr. Waktu tercepat dilaporkan saat mengkompilasi tanpa /clr atau jika fungsi virtual dideklarasikan dengan __clrcall.

Kode

// 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");
}

Output sampel

4.2 seconds
after calling struct S

Baca juga

Rakitan Campuran (Asli dan Terkelola)