【发布时间】:2021-08-26 07:24:15
【问题描述】:
我已经设置了一个本地 WCF 服务,它自托管在控制台应用程序中,使用 NetNamedPipeBinding 进行客户端访问。
为了调用服务,我引用了library.dll,其中我有以下方法:
public static string GetLevel(Point p)
{
ChannelFactory<IService> pipeFactory = new ChannelFactory<IService>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/PTS_Service"));
IService pipeProxy = pipeFactory.CreateChannel();
string result = pipeProxy.GetLevel(p);
((IClientChannel)pipeProxy).Close();
pipeFactory.Close();
}
GetLevel() 命令根据 Point(X,Y,Z) p 的 Z 坐标从存储在服务中的列表中返回一个字符串。
如果从上面的控制台应用程序调用该方法,则此方法有效并且总速度为 8 毫秒。
但是,当从另一个 app.exe 或 plugin.dll(由外部程序加载)调用来自 library.dll 的相同方法时,时间会急剧增加。上面这5行代码我已经看完了:
- consoleHost.exe:0 - 3 - 6 - 7 - 8
- app.exe : 89 - 155 - 248 - 259 - 271
- plugin.dll : 439 - 723 - 1210 - 1229 - 1245
时间不应该相同,不取决于谁拨打library.dll吗?
编辑
由于我已经取消了仅从正在运行的服务中检索字符串的所有方法,我相信问题在于 channelFactory 的第一次创建运行,同一应用程序/插件运行中的所有后续调用在时间上都是相等的。
我知道第一次调用速度较慢,但我发现这在新应用中大约是 30 毫秒,在我的插件中大约是 900 毫秒,我相信还有其他原因造成了这种情况。
我发现了一个类似延迟的问题:
解决方案是将LoaderOptimizationAttribute 设置为MultiDomain 的First WCF connection made in new AppDomain is very slow。是否有可能每次插件运行时都必须进行 JIT 编译而不是使用本机代码?
我尝试在 consoleHost.exe 的 main 上方添加此代码,但在插件运行时没有看到任何增益。这可能是因为两者之间的外部程序,有没有办法解决这个问题?假设我的插件可以在它想要访问服务时创建一个新的 Appdomain 并从这个新的 Appdomain 中调用我的library.dll 中的上述方法还是没有意义?
EDIT2
我使用 cmets 中建议的分析程序记录了 JIT 编译所花费的时间,这为 JIT 编译提供了 700 毫秒,插件的总执行时间为 800 毫秒。
我使用 ngen 预编译 library.dll 以创建本机映像 .ni.dll。我在进程资源管理器中看到该图像是由外部程序加载的,尽管插件没有时间增益?据我了解,插件仍然会 JIT 编译不应该是有原因的,还是我做错了什么?
在 VS 中调试时,我还注意到控制台和应用程序只加载一些程序集,插件在每次创建或修改插件实例时都会加载和卸载。我相信这是插件的工作方式,不应该解释第一次执行时间的差异吗?
【问题讨论】:
-
会不会是对dll的调用是对dll的第一次调用?在这种情况下,加载 dll 及其依赖项可能是第一次调用所需时间的一部分
-
dll是之前访问的(非服务方法)所以应该已经加载了吗?此外,为什么应用程序不会这么慢?
-
即时编译不能那样工作。它只编译被调用的函数,就在它们被调用之前。所以不是每个组件。将时间安排在您在 dll 内显示的代码周围。为了更准确地测量。
-
好的,我明白了。那么如何防止插件中的JIT编译呢?调用的每一行/函数的时间确实更大,如引用链接中所述。
-
我看到两个选项(如果这确实是问题所在): 1:在程序开始时调用所有重要功能一次或 2:使用 NGen.exe 预编译代码。 docs.microsoft.com/en-us/dotnet/framework/tools/…