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