我使用委托从非托管代码调用托管代码。当我调用默认 AppDomain 中的托管代码时,我测量到每次调用的平均时间为 5.4 纳秒。当我调用第二个 AppDomain 时,我测量到每次调用需要 194 纳秒。 (默认VS2017 x86发布配置,不在调试器下运行)。
为什么调用非默认 AppDomain 时性能会低得多?由于我来自非托管端,它不了解 AppDomains,因此我希望直接调用目标域。但是,性能下降意味着委托正在调用默认域,然后编组到真正的目标。我确实看到了UM2MDoADCallBack
当逐步进行反汇编时。哪个显示在下面WrongAppDomain:
in UMThunkStub.asm https://github.com/dotnet/coreclr/blob/master/src/vm/amd64/UMThunkStub.asm
如何防止这种不必要的封送并直接调用特定的 AppDomain?
我用来测试这个的代码如下。
#pragma unmanaged
#include <wtypes.h>
#include <cstdint>
#include <cwchar>
typedef void (__stdcall *ManagedUpdatePtr)();
struct ProfileSample
{
static uint64_t frequency;
uint64_t startTick;
wchar_t* name;
int count;
ProfileSample(wchar_t* name_, int count_)
{
name = name_;
count = count_;
LARGE_INTEGER win32_startTick;
QueryPerformanceCounter(&win32_startTick);
startTick = win32_startTick.QuadPart;
}
~ProfileSample()
{
LARGE_INTEGER win32_endTick;
QueryPerformanceCounter(&win32_endTick);
uint64_t endTick = win32_endTick.QuadPart;
uint64_t deltaTicks = endTick - startTick;
double nanoseconds = (double) deltaTicks / (double) frequency * 1000000000.0 / count;
wchar_t buffer[128];
swprintf(buffer, _countof(buffer), L"%s - %.4f ns\n", name, nanoseconds);
OutputDebugStringW(buffer);
if (!IsDebuggerPresent())
MessageBoxW(nullptr, buffer, nullptr, 0);
}
};
uint64_t ProfileSample::frequency = 0;
int CALLBACK
WinMain(HINSTANCE, HINSTANCE, PSTR, INT)
{
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
ProfileSample::frequency = frequency.QuadPart;
ManagedUpdatePtr GetManagedUpdatePtr();
auto managedUpdate = GetManagedUpdatePtr();
//Warm stuff up
for ( size_t i = 0; i < 100; i++ )
managedUpdate();
const int num = 10000000;
{
ProfileSample p(L"ManagedUpdate", num);
for ( size_t i = 0; i < num; i++ )
managedUpdate();
}
return 0;
}
#pragma managed
using namespace System;
using namespace System::Diagnostics;
using namespace System::Runtime::InteropServices;
ref struct ManagedObject : MarshalByRefObject
{
ManagedUpdatePtr
GetManagedUpdatePtr()
{
auto delegate = gcnew Action(this, &ManagedObject::ManagedUpdate);
IntPtr fPtr = Marshal::GetFunctionPointerForDelegate(delegate);
return (ManagedUpdatePtr) fPtr.ToPointer();
}
void ManagedUpdate()
{
//Debug::WriteLine("\n\nManagedUpdate ({0})", (Object^) AppDomain::CurrentDomain->FriendlyName);
}
};
ManagedUpdatePtr
GetManagedUpdatePtr()
{
auto pluginDomain = AppDomain::CreateDomain("Plugin Domain");
auto managedObject = (ManagedObject^) pluginDomain->CreateInstanceAndUnwrap("ManagedHelper", "ManagedObject");
return managedObject->GetManagedUpdatePtr();
}