Sdílet prostřednictvím


Dvojitý převod adres na jinou bitovou šířku (jazyk C++)

Dvojitý převod adres na jinou bitovou šířku odkazující na ztrátu výkonu, můžete vyzkoušet při volání funkce ve spravovaném kontextu volání spravované funkce jazyka Visual C++ a kde provádění programu volá nativní vstupní bod funkce v pořadí volání spravované funkce. Toto téma popisuje, kde dochází k dvojitému převodu adres na jinou bitovou šířku a jak tomu můžete zabránit ke zlepšení výkon.

Poznámky

Ve výchozím nastavení při kompilaci s /clr (ne /clr:pure), způsobí definice spravované funkce to, že kompilátor vygeneruje spravovaný vstupní bod a nativní vstupní bod. To umožňuje volání spravované funkce z nativních a spravovaných volání stran. Avšak pokud existuje nativní vstupní bod, může být vstupním bodem pro všechna volání funkce. Pokud je volání funkce spravované, zavolá poté nativní vstupní bod spravovaný vstupní bod. Ve skutečnosti jsou potřeba dvě volání pro vyvolání funkce (a proto tedy dvojitý převod adres na jinou bitovou šířku). Například virtuální funkce jsou vždy volány prostřednictvím nativního vstupního bodu.

Jedno rozlišení je pro oznámení kompilátoru o negenerování nativního vstupního bodu pro spravovanou funkci, takže bude funkce volaná pouze ze spravovaného kontextu za použití konvence volání __clrcall.

Podobně pokud jste exportovali spravovanou funkci (dllexport, dllimport), je generován nativní vstupní bod a všechny funkce, které importují a volají, když bude funkce volána prostřednictvím nativního vstupního bodu. Chcete-li se v této situaci vyhnout dvojitému převodu adres na jinou bitovou šířku, nepoužívejte nativní export/import sémantiku; jednoduše odkazujte metadata prostřednictvím #using (viz The #using Directive).

Kompilátor snížit zbytečné dvojité Převod byl aktualizován. Například všechny funkce se spravovaným typem v podpisu (včetně návratového typu) budou implicitně označeny jako __clrcall. Další informace o odstranění dvojité jádro viz https://msdn.microsoft.com/msdnmag/issues/05/01/COptimizations/default.aspx.

Příklad

Popis

Následující příklad znázorňuje dvojitý převod adres na jinou bitovou šířku. Při nativní kompilaci (bez /clr), volání virtuální funkce v main generuje jedno volání kopie T konstruktoru a jedno volání destruktoru. Podobného chování je dosaženo při deklaraci virtuální funkce s /clr a __clrcall. Nicméně když právě kompilujete s /clr, volání funkce generuje volání kopie konstruktoru, zde je ale další volání kopie konstruktoru kvůli z nativního do spravovaného převodu adres na jinou bitovou šířku.

Kód

// 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");
}

Vzorový výstup

__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)

Příklad

Popis

Předchozí příklad ukazuje existenci dvojitého převodu adres na jinou bitovou šířku. Tento příklad ukazuje její vliv. Smyčka for volá virtuální funkci a program oznamuje dobu spuštění. Nejpomalejší čas je vykazován, když je program zkompilován s /clr. Nejrychlejší časy jsou vykazovány při kompilaci bez /clr nebo pokud je virtuální funkce deklarována s __clrcall.

Kód

// 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");
}

Vzorový výstup

4.2 seconds
after calling struct S

Viz také

Koncepty

Smíšená (nativní a spravovaná) sestavení