Çift Dönüştürme (C++)
Çift çalıştırma, yönetilen bağlamdaki bir işlev çağrısı Visual C++ yönetilen işlevini çağırdığında ve program yürütmenin yönetilen işlevi çağırmak için işlevin yerel giriş noktasını çağırdığında karşılaşabileceğiniz performans kaybını ifade eder. Bu konu, çift pernkingin nerede gerçekleştiğini ve performansı geliştirmek için bundan nasıl kaçınabileceğinizi açıklar.
Açıklamalar
Varsayılan olarak, /clr ile derlenirken yönetilen işlevin tanımı derleyicinin yönetilen bir giriş noktası ve yerel giriş noktası oluşturmasına neden olur. Bu, yönetilen işlevin yerel ve yönetilen çağrı sitelerinden çağrılmasını sağlar. Ancak, yerel bir giriş noktası mevcut olduğunda, işleve yapılan tüm çağrılar için giriş noktası olabilir. Bir çağrı işlevi yönetiliyorsa, yerel giriş noktası yönetilen giriş noktasını çağırır. Aslında, işlevi çağırmak için iki çağrı gerekir (bu nedenle, çift thunking). Örneğin, sanal işlevler her zaman yerel bir giriş noktası aracılığıyla çağrılır.
Çözümlerden biri, derleyiciye yönetilen bir işlev için yerel giriş noktası oluşturmaması gerektiğini, işlevin yalnızca __clrcall çağırma kuralı kullanılarak yönetilen bağlamdan çağrılacağını söylemektir.
Benzer şekilde, yönetilen bir işlevi dışarı aktarırsanız (dllexport, dllimport) yerel bir giriş noktası oluşturulur ve bu işlevi içeri aktaran ve çağıran herhangi bir işlev yerel giriş noktası üzerinden çağrılır. Bu durumda iki kez thunking önlemek için yerel dışarı/içeri aktarma semantiği kullanmayın; #using
(bkz . #using Yönergesi).
Derleyici gereksiz çift pernking'i azaltacak şekilde güncelleştirildi. Örneğin, imzada yönetilen türe sahip tüm işlevler (dönüş türü dahil) örtük olarak olarak __clrcall
işaretlenir.
Örnek: Çift thunking
Açıklama
Aşağıdaki örnekte çift thunking gösterilmektedir. Yerel olarak derlendiğinde (/clr olmadan), içindeki main
sanal işleve yapılan çağrı, 'nin kopya oluşturucusunun T
bir çağrısını ve yıkıcıya yapılan bir çağrıyı oluşturur. Sanal işlev /clr ve __clrcall
ile bildirildiğinde benzer davranış elde edilir. Ancak, yalnızca /clr ile derlendiğinde, işlev çağrısı kopya oluşturucuya bir çağrı oluşturur, ancak yerelden yönetilen thunk nedeniyle kopya oluşturucusunun başka bir çağrısı vardır.
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");
}
Örnek Çıkış
__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)
Örnek: Çift thunking etkisi
Açıklama
Önceki örnekte çift thunking varlığı gösterilmiştir. Bu örnek etkisini gösterir. Döngü for
sanal işlevi çağırır ve program yürütme süresini raporlar. Program /clr ile derlendiğinde en yavaş süre bildirilir. /clr olmadan derleme yapılırken veya sanal işlev ile __clrcall
bildirilirken en hızlı süreler bildirilir.
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");
}
Örnek Çıkış
4.2 seconds
after calling struct S