Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Dubbel thunking avser den prestandaförlust som du kan uppleva när ett funktionsanrop i en hanterad miljö anropar en Visual C++ hanterad funktion och där programutförande anropar funktionens ursprungliga startpunkt för att anropa den hanterade funktionen. I det här avsnittet beskrivs var dubbel dunkning inträffar och hur du kan undvika det för att förbättra prestandan.
Anmärkningar
När du kompilerar med /clr gör definitionen av en hanterad funktion som standard att kompilatorn genererar en hanterad startpunkt och en intern startpunkt. På så sätt kan den hanterade funktionen anropas från interna och hanterade anropsplatser. Men när det finns en intern startpunkt kan den vara startpunkten för alla anrop till funktionen. Om en anropande funktion hanteras, kommer den ursprungliga startpunkten att anropa den hanterade startpunkten. Faktiskt krävs två anrop för att anropa funktionen (vilket innebär dubbel anropsteknik). Virtuella funktioner anropas till exempel alltid via en intern startpunkt.
En lösning är att instruera kompilatorn att inte generera en intern startpunkt för en hanterad funktion, att funktionen endast anropas från en hanterad kontext med hjälp av __clrcall anropande konvention.
Om du exporterar (dllexport, dllimport) en hanterad funktion genereras en intern startpunkt och alla funktioner som importerar och anropar den funktionen anropas via den interna startpunkten. Använd inte intern export/import-semantik för att undvika dubbel thunking i den här situationen. bara referera till metadata via #using (se #using direktiv).
Kompilatorn har uppdaterats för att minska onödiga dubbla dunsningar. Till exempel markeras alla funktioner med en hanterad typ i signaturen (inklusive returtyp) implicit som __clrcall.
Exempel: Dubbel thunking
Beskrivning
Följande exempel visar dubbla dunsningar. När det kompileras internt (utan /clr) genererar anropet till den virtuella funktionen i main ett anrop till Tkopieringskonstruktorn och ett anrop till destruktorn. Liknande beteende uppnås när den virtuella funktionen deklareras med /clr och __clrcall. Men när det bara kompileras med /clr genererar funktionsanropet ett anrop till kopieringskonstruktorn, men det sker också ett annat anrop till kopieringskonstruktorn på grund av en native-till-managed thunk.
Kod
// 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");
}
Exempelutdata
__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)
Exempel: Effekten av dubbel thunking
Beskrivning
Det tidigare exemplet visade förekomsten av dubbel thunking. Det här exemplet visar dess effekt. Loopen for anropar den virtuella funktionen och programmet rapporterar körningstid. Den långsammaste tiden rapporteras när programmet kompileras med /clr. De snabbaste tiderna rapporteras vid kompilering utan /clr eller om den virtuella funktionen deklareras med __clrcall.
Kod
// 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");
}
Exempelutdata
4.2 seconds
after calling struct S