Двойное преобразование (С++)

Двойное thunking ссылается на потерю производительности, которую можно испытать, когда вызов функции в управляемом контексте вызывает управляемую функцию Visual C++ и где выполнение программы вызывает собственную точку входа функции, чтобы вызвать управляемую функцию. В этом разделе описывается, где происходит двойная отсутствование и как можно избежать повышения производительности.

Замечания

По умолчанию при компиляции с помощью /clr определение управляемой функции приводит компилятору к созданию управляемой точки входа и собственной точки входа. Это позволяет вызывать управляемую функцию из собственных и управляемых сайтов вызовов. Однако при наличии собственной точки входа она может быть точкой входа для всех вызовов функции. Если вызывающая функция управляется, то собственная точка входа вызовет управляемую точку входа. В действительности для вызова функции требуются два вызова (следовательно, двойное thunking). Например, виртуальные функции всегда вызываются через собственную точку входа.

Одно решение заключается в том, чтобы компилятор не создавал собственную точку входа для управляемой функции, что функция будет вызываться только из управляемого контекста с помощью соглашения о вызове __clrcall .

Аналогичным образом, при экспорте (dllexport, dllimport) управляемой функции создается собственная точка входа и любая функция, которая импортирует и вызывает эту функцию через собственную точку входа. Чтобы избежать двойного переключения в этой ситуации, не используйте собственную семантику экспорта и импорта; просто сослаться на метаданные с помощью #using (см . директиву #using).

Компилятор был обновлен, чтобы уменьшить ненужные двойные thunking. Например, любая функция с управляемым типом в сигнатуре (включая возвращаемый тип) неявно будет помечена как __clrcall.

Пример: двойное thunking

Description

В следующем примере показано двойное тюнкирование. При компиляции собственного кода (без /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)

Пример: эффект двойного тонинга

Description

В предыдущем примере показано существование двойного тонинга. В этом примере показан его эффект. Цикл 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

См. также

Смешанные (собственные и управляемые) сборки