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