[英]FileNotFoundException when calling remote C# dll from C++/CLR
[英]How does the CLR avoid thunking when calling c++ functions?
MSDN声明 :
无论使用何种互操作技术,每次托管函数调用非托管函数时都需要特殊的转换序列(称为thunks),反之亦然。 这些thunk是由Visual C ++编译器自动插入的,但重要的是要记住,累积起来,这些转换在性能方面可能很昂贵。
然而,CLR肯定会一直调用C ++和Win32函数。 为了处理文件/网络/窗口以及几乎任何其他内容,必须调用非托管代码。 它是如何摆脱分块惩罚的?
这是一个用C ++ / CLI编写的实验,可能有助于描述我的问题:
#define REPS 10000000
#pragma unmanaged
void go1() {
for (int i = 0; i < REPS; i++)
pow(i, 3);
}
#pragma managed
void go2() {
for (int i = 0; i < REPS; i++)
pow(i, 3);
}
void go3() {
for (int i = 0; i < REPS; i++)
Math::Pow(i, 3);
}
public ref class C1 {
public:
static void Go() {
auto sw = Stopwatch::StartNew();
go1();
Console::WriteLine(sw->ElapsedMilliseconds);
sw->Restart();
go2();
Console::WriteLine(sw->ElapsedMilliseconds);
sw->Restart();
go3();
Console::WriteLine(sw->ElapsedMilliseconds);
}
};
//Go is called from a C# app
结果是(一致地):
405 (go1 - pure C++)
818 (go2 - managed code calling C++)
289 (go3 - pure managed)
为什么go3比go1更快有点神秘,但这不是我的问题。 我的问题是,我们从go1&go2看到,thunking惩罚增加了400ms。 go3如何摆脱这种惩罚, 因为它调用C ++来进行实际计算?
即使这个实验由于某种原因无效,我的问题仍然存在 - 每次调用C ++ / Win32时,CLR是否真的有一个thunking惩罚?
基准测试是一种黑色艺术,你在这里得到了一些误导性的结果。 运行Release版本非常重要,如果你这样做,那么你现在会注意到go1()不再需要时间了。 本机代码优化器具有它的特殊知识,如果你不使用它的结果,那么它完全消除它。
您必须更改代码才能获得可靠的结果。 首先在Go()测试体周围放置一个循环,重复至少20次。 这消除了jitting和缓存开销,并有助于查看大的标准偏差。 敲掉REPS 0,这样你就不用等太久了。 赞成工具>选项>调试>常规,“抑制JIT优化”未选中。 更改代码,我建议:
__declspec(noinline)
double go1() {
double sum = 0;
for (int i = 0; i < REPS; i++)
sum += pow(i, 3);
return sum;
}
注意sum变量如何强制优化器保持调用,使用__declspec可以防止删除整个函数并避免污染Go()体。 对go2和go3执行相同操作,使用[MethodImpl(MethodImplOptions :: NoInlining)]。
我在笔记本电脑上看到的结果:x64:75,84,84,x86:73,89,89 + 5 / -3毫秒。
工作中有三种不同的机制:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.