Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Double thunking refere-se à perda de desempenho que se pode sentir quando uma chamada de função num contexto gerido invoca uma função gerida do Visual C++ e a execução do programa ativa o ponto de entrada nativo da função, a fim de chamar a função gerida. Este tópico aborda onde ocorre o double thunking e como pode ser evitado para melhorar o desempenho.
Observações
Por padrão, ao compilar com /clr, a definição de uma função gerenciada faz com que o compilador gere um ponto de entrada gerenciado e um ponto de entrada nativo. Isso permite que a função gerida seja chamada a partir de locais de chamada nativos e geridos. No entanto, quando existe um ponto de entrada nativo, ele pode ser o ponto de entrada para todas as chamadas para a função. Se uma função de chamada for gerenciada, o ponto de entrada nativo chamará o ponto de entrada gerenciado. Na verdade, duas chamadas são necessárias para invocar a função (portanto, duplo thunking). Por exemplo, as funções virtuais são sempre chamadas através de um ponto de entrada nativo.
Uma resolução é dizer ao compilador para não gerar um ponto de entrada nativo para uma função gerenciada, que a função só será chamada a partir de um contexto gerenciado, usando a convenção de chamada __clrcall .
Da mesma forma, se você exportar (dllexport, dllimport) uma função gerenciada, um ponto de entrada nativo será gerado e qualquer função que importar e chamar essa função chamará através do ponto de entrada nativo. Para evitar o "double thunking" nesta situação, não utilize a semântica nativa de exportação/importação; basta referenciar os metadados através de #using (ver Diretiva #using).
O compilador foi atualizado para reduzir o duplo thunking desnecessário. Por exemplo, qualquer função com um tipo gerenciado na assinatura (incluindo o tipo de retorno) será implicitamente marcada como __clrcall.
Exemplo: Double thunking (processo de duplo atraso)
Descrição
O exemplo a seguir demonstra o processo de double thunking. Quando compilada como nativo (sem /clr), a chamada para a função virtual em main gera uma chamada para o construtor de cópia de T e uma chamada para o destrutor. Comportamento semelhante é alcançado quando a função virtual é declarada com /clr e __clrcall. No entanto, quando compilada apenas 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 ao thunk 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 amostra
__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: Efeito de duplo debulhar
Descrição
A amostra anterior demonstrou a existência de duplo thunking. Este exemplo mostra o seu efeito. O for loop chama a função virtual e o programa relata o tempo de execução. O tempo mais lento é relatado quando o programa é compilado com /clr. Os tempos mais rápidos são relatados ao compilar 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 amostra
4.2 seconds
after calling struct S