Podwójne pośredniczeniem (C++)
Podwójne pośredniczeniem odnosi się do utraty wydajności, które mogą wystąpić podczas wywołania funkcji w wywołaniach zarządzanych kontekście Visual C++ zarządzane funkcji i gdzie program wykonanie wywołuje punktu wejścia macierzystych funkcji do wywołania funkcji zarządzanych.W tym temacie omówiono, gdzie występuje podwójna pośredniczeniem i jak można uniknąć go poprawić wydajność.
Uwagi
Domyślnie podczas kompilacji z /clr (nie /clr:pure), definicja zarządzanych funkcji powoduje, że kompilator generowania zarządzany punkt wejścia i punkt wejścia macierzystym.Dzięki temu zarządzanych funkcji do wywołania z witryn macierzystych i zarządzanych wywołanie.Jednakże gdy istnieje punkt wejścia macierzystym, może być punkt wejścia dla wszystkich wywołań funkcji.Wywołanie funkcji jest zarządzany, punkt wejścia macierzystym następnie wywołuje punktu wejścia zarządzanych.W efekcie dwa połączenia są wymagane do wywołania funkcji (dwukrotnie w związku z tym pośredniczeniem).Na przykład funkcje wirtualnych zawsze są wywoływane przez punkt wejścia macierzystym.
Jeden rozdzielczość jest kompilatorowi nie generować macierzystym wlotu funkcja zarządzanych, że funkcja będzie zostać wywołana tylko z zarządzanego kontekstu, za pomocą __clrcall konwencji wywoływania.
Podobnie podczas eksportowania (dllexport, dllimport) zarządzanym funkcji punktu wejścia macierzystym jest generowany i funkcji przywozu i wywołuje funkcję ten będzie wywoływać przez punkt wejścia macierzystym.Aby uniknąć podwójnego pośredniczeniem w tej sytuacji, nie należy używać semantykę macierzystym wywóz/przywóz; po prostu odwołać metadanych za pomocą #using (zobacz # za pomocą dyrektywy (C++)).
Kompilator został zaktualizowany do zmniejszenia niepotrzebne pośredniczeniem podwójne.Na przykład funkcji dowolnego typu zarządzanego w podpisie (w tym typ zwracany), niejawnie zostanie oznaczony jako __clrcall.Aby uzyskać więcej informacji na zniesienie podwójnego thunk, zobacz https://msdn.microsoft.com/msdnmag/issues/05/01/COptimizations/default.aspx.
Przykład
Opis
Następujące przykładowe demonstruje pośredniczeniem podwójne.Gdy kompilowany macierzysty (bez /clr), wywołanie funkcji wirtualnych w main generuje jedno wywołanie Tprzez kopiowanie konstruktora i jedno wywołanie destruktora.Podobne zachowanie jest osiągnięty, jeżeli zadeklarowano funkcję wirtualną z /clr i __clrcall.Jednakże gdy tylko skompilowany z /clr, wywołanie funkcji generuje wywołania konstruktora kopii, ale istnieje inne wywołanie konstruktora kopii spowodowane thunk macierzystego zarządzane.
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");
}
Przykładowe dane wyjściowe
__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)
Przykład
Opis
Poprzedni przykład wykazało istnienie pośredniczeniem podwójne.Ten przykład pokazuje jego wpływ.for Pętli wywołuje funkcję wirtualną i czasu wykonywania program raportów.Najwolniejszy czas jest zgłaszany, gdy program jest kompilowany z /clr.Najszybsze razy są zgłaszane podczas kompilacji bez /clr lub jeżeli zadeklarowano funkcję wirtualną z __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");
}
Przykładowe dane wyjściowe
4.2 seconds
after calling struct S