Compartilhar via


Duplo conversão (C++)

Conversão dupla refere-se a perda de desempenho que pode ocorrer quando uma chamada de função chamadas um contexto gerenciado Visual C++ gerenciado função e execução de programas onde chama o ponto de entrada nativo da função para chamar a função gerenciada.Este tópico discute onde ocorre a conversão de double e como evitá-lo para melhorar o desempenho.

Comentários

Por padrão, quando a compilação com /clr (não /clr:pure), a definição de uma função gerenciada faz o compilador gerar um ponto de entrada gerenciado e um ponto de entrada nativo.Isso permite que a função gerenciada ser chamado a partir de sites de chamada gerenciado e nativo.No entanto, quando existe um ponto de entrada nativo, pode ser o ponto de entrada para todas as chamadas para a função.Se uma função chamada é gerenciada, o ponto de entrada nativo então chamar o ponto de entrada gerenciado.Na verdade, duas chamadas são necessários para invocar a função (portanto, duplas de conversão).Por exemplo, funções virtuais são sempre chamadas através de um ponto de entrada nativo.

É uma resolução informar ao compilador para não gerar um ponto de entrada nativo de uma função gerenciada que a função apenas ser chamada de um contexto gerenciado usando o __clrcall convenção de chamada.

Da mesma forma, se você exportar (dllexport, dllimport) uma função gerenciada, um ponto de entrada nativo é gerado e qualquer função que importa e chama essa função será chamada através do ponto de entrada nativo.Para evitar a conversão dupla nessa situação, não use semântica nativo de exportação e importação; basta consultar os metadados por meio de #using (consulte # usando diretiva (C++)).

O compilador foi atualizado para reduzir a conversão dupla desnecessários.Por exemplo, qualquer função com um tipo gerenciado na assinatura (incluindo o tipo de retorno) implicitamente será marcada como __clrcall.Para obter mais informações sobre a eliminação de dupla conversão, consulte https://msdn.microsoft.com/msdnmag/issues/05/01/COptimizations/default.aspx.

Exemplo

ms235292.collapse_all(pt-br,VS.110).gifDescrição

O exemplo a seguir demonstra conversão dupla.Quando compilado nativo (sem /clr), a chamada de função virtual na main gera uma chamada para Tdo copiar construtor e uma chamada para o destruidor.Comportamento semelhante é obtido quando a função virtual é declarada com /clr e __clrcall.No entanto, quando apenas compilado com /clr, a chamada de função gera uma chamada para o construtor de cópia, mas há outra chamada para o construtor de cópia devido a conversão nativo gerenciado.

ms235292.collapse_all(pt-br,VS.110).gifCó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");
}

ms235292.collapse_all(pt-br,VS.110).gifExemplo de saída

__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

ms235292.collapse_all(pt-br,VS.110).gifDescrição

O exemplo anterior demonstrou a existência de conversão dupla.Este exemplo mostra seu efeito.O for loop chama a função virtual e o tempo de execução de relatórios do programa.O tempo mais lento é relatado quando o programa é compilado com /clr.Vezes mais rápidos são relatados durante a compilação sem /clr ou se a função virtual é declarada com __clrcall.

ms235292.collapse_all(pt-br,VS.110).gifCó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");
}

ms235292.collapse_all(pt-br,VS.110).gifExemplo de saída

4.2 seconds
after calling struct S

Consulte também

Conceitos

Mistos Assemblies (nativos e gerenciados)