Partilhar via


Duplo conversão (C++)

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

Comentários

Por padrão, durante a compilação de /clr (não /clr:pure), a definição de uma função gerenciada faz com que o compilador gerar um ponto de entrada gerenciado e um ponto de entrada nativo. Desse modo, a função do gerenciado a ser chamado de sites de telefonar nativa e gerenciada.No entanto, quando houver um ponto de entrada nativo, pode ser o ponto de entrada de todas as chamadas para a função.Se uma função telefonar for gerenciada, o ponto de entrada nativo, em seguida, 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 virtual são sempre chamadas através de um ponto de entrada nativo.

Uma resolução é informar ao compilador que não geram um ponto de entrada nativo para uma função gerenciada, que a função apenas ser chamada em 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 entrará em contato com qualquer função que importa e chama essa função pelo ponto de entrada nativo. Para evitar a conversão dupla nessa situação, escolha não usar semântica nativo de exportar e importação; simplesmente fazer referência o metadados por meio de #using (consulte a The # Using Directive).

In Visual C++ 2005 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 sistema autônomo __clrcall. Para obter mais informações sobre conversão dupla de eliminação, consulte https://msdn.Microsoft.com/msdnmag/issues/05/01/COptimizations/padrão.aspx.

Exemplo

Descrição

O exemplo a seguir mostra a conversão dupla.Quando compilada nativo (sem /clr), a telefonar para a função virtual em main gera uma telefonar para Tdo construtor de cópia e uma telefonar para o destruidor. Comportamento semelhante é obtido quando a função virtual é declarada com /clr e __clrcall. No entanto, quando apenas compilado com /clr, a telefonar de função gera uma telefonar para o construtor de cópia mas há outra telefonar para o construtor de cópia, devido à conversão 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 conversão dupla.Esse exemplo mostra o efeito.The for loop chama a função virtual e o time de execução de relatórios do programa. O time mais lento é relatado quando o programa será compilado com /clr. Os tempos mais rápidos são relatados durante a compilação sem /clr ou se a função virtual for 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

Misto (nativo e gerenciado) assemblies