Partilhar via


Conversão dupla (C++)

Thunking duplo refere-se à perda de desempenho que podem surgir quando uma chamada de função em um contexto gerenciado chama uma função gerenciada Visual C++ e onde a execução do programa chamar o ponto de entrada nativo de função para chamar a função gerenciada. Este tópico discute onde thunking vezes ocorre e como você pode evitar o para melhorar o desempenho.

Comentários

Por padrão, ao criar com /clr (não /clr:pure), a definição de uma função gerenciada faz com que o compilador gerencia um ponto de entrada gerenciado e um ponto de entrada nativo. Isso permite que a função gerenciada é chamada nativo e de locais gerenciados de chamada. Porém, quando um ponto de entrada nativa, pode ser o ponto de entrada para todas as chamadas para a função. Se uma função de chamada é gerenciada, o ponto de entrada nativo chamará o ponto de entrada gerenciado. De fato, duas chamadas são necessários invocar a função (consequentemente, double que thunking). Por exemplo, as funções virtuais são sempre chamadas por um ponto de entrada nativo.

Uma resolução dizer o compilador para gerar um ponto de entrada nativo para uma função gerenciada, do qual a função será chamada de apenas um contexto gerenciada, com __clrcall que chama a convenção.

Da mesma forma, se você exportar ()dllexport, dllimportuma função gerenciada, um ponto de entrada nativo é gerado e qualquer função que importar e chamadas que a função chamará pelo ponto de entrada nativo. Para evitar o valor double que thunking nessa situação, não use a semântica nativo de importação/exportação; referenciar apenas os metadados por meio de #using (consulte Diretiva #using (C++)).

O compilador foi atualizado para reduzir thunking duplo desnecessário. Por exemplo, qualquer função com um gerenciado na assinatura (que inclui o tipo de retorno) será marcada em implicitamente como __clrcall. Para obter mais informações sobre como eliminar de dois thunk, consulte https://msdn.microsoft.com/msdnmag/issues/05/01/COptimizations/default.aspx.

Exemplo

Descrição

O exemplo a seguir demonstra thunking duplo. Quando criado o nativo (sem /clr), a chamada para a função virtual em main gerenciar uma chamada para o construtor de cópia de T e uma chamada para destruidor. O comportamento semelhante é obtido quando a função virtual é declarada com /clr e __clrcall. No entanto, quando criada apenas com /clr, a chamada de função gerenciar uma chamada para o construtor de cópia mas há outra chamada para o construtor de cópia devido ao thunk nativo-à- gerenciado.

Código

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

Saída de Exemplo

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

Exemplo

Descrição

O exemplo anterior demonstrou a existência de thunking duplo. Este exemplo mostra o efeito. O loop de for chama a função virtual e o tempo de execução de relatórios de programa. O tempo mais lentos são relatados quando o programa é criado com /clr. O tempo mais rápidos são relatados no construir sem /clr ou se a função virtual é declarada com __clrcall.

Código

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

Saída de Exemplo

4.2 seconds
after calling struct S

Consulte também

Conceitos

Assemblies mistos (nativos e gerenciados)