Dupla de conversão (C++)

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

Remarks

Por padrão, quando compilando com /clr (não /clr:pure), a definição de uma função gerenciada faz com que o compilador gere um ponto de entrada gerenciado e um ponto de entrada nativa. Isso permite que a função gerenciada ser chamado a partir de sites de chamada nativa e gerenciada. No entanto, quando não 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, em seguida, chamará o ponto de entrada gerenciado. Na verdade, duas chamadas são necessárias para invocar a função (portanto, duplas de conversão). Por exemplo, as funções virtuais sempre são chamadas por meio de um ponto de entrada nativo.

Uma resolução é informar ao compilador para não gerar um ponto de entrada nativo para uma função gerenciada, que a função só ser chamada em um contexto de gerenciado usando o __clrcall chamando convenção.

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 entrará em contato por meio do ponto de entrada nativa. Para evitar a conversão dupla nessa situação, não usar semântica de exportação e importação nativo; basta consultar os metadados por meio de #using (consulte The #using Directive).

O compilador foi atualizado para reduzir a conversão de duplo 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 conversão duplo, consulte https://msdn.microsoft.com/msdnmag/issues/05/01/COptimizations/default.aspx.

Example

Description

O exemplo a seguir demonstra a conversão dupla. Quando compilado nativo (sem /clr), a chamada à função virtual em main gera uma chamada para Tdo construtor de cópia 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 à nativo gerenciado thunk.

Code

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

Sample Output

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

Example

Description

O exemplo anterior demonstrou a existência de conversão dupla. Este exemplo mostra o efeito. O for loop chama a função virtual e o programa informa o tempo de execução. O tempo mais lento é relatado quando o programa é 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.

Code

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

Sample Output

4.2 seconds
after calling struct S

Consulte também

Conceitos

Mistos de Assemblies (nativos e gerenciados)