【发布时间】:2019-05-07 16:54:18
【问题描述】:
我们正在将我们的工作站从 Win7 升级到 Win10。在调查性能下降的报告时,我得出的结论是它是由第三方安装的 WH_CALLWNDPROC 挂钩引起的。
我根据以下测试应用程序的结果得出这个结论(Done in Delphi 10 Seattle)
procedure TForm3.Button1Click(Sender: TObject);
var
I: Integer;
SW : TStopWatch;
begin
sw := TStopWatch.StartNew;
for I := 0 to 1000000 do
begin
if Combobox1.ItemIndex > 0 then
Exit;
end;
sw.Stop;
ShowMessage(sw.ElapsedMilliseconds.ToString);
end;
(对于那些不熟悉 Delphi 的人,TStopwatch 使用 QueryPerformanceFrequency/QueryPerformanceCounter API 来获取经过的时间)
这个方法的执行时间是
- Win10 : 1485 毫秒
- Win7 : 4996 毫秒
(注意:两台机器的硬件完全不同,无法真正相互比较)。
现在,如果我在执行相同的代码之前添加一个钩子
function MySystemWndProcHook(Code: Integer; wParam: WParam; lParam: LParam): LRESULT; stdcall;
begin
Result := CallNextHookEx(FHook, Code, wParam, LParam);
end;
procedure TForm3.FormCreate(Sender: TObject);
begin
FHook := SetWindowsHookEx(WH_CALLWNDPROC, @MySystemWndProcHook, 0, GetCurrentThreadId)
end;
现在的执行时间变成了:
- Win10 : 19552 毫秒(大约长 1300%)
- Win7:8682 毫秒(大约长 75%)
现在,正如我所提到的,两个工作站都在不同的硬件上,但我不相信仅凭这一点就可以解释差异。 Win10 有 i7 cpu,而 Win7 有 i3。如果有的话,我希望 i3 受到更大的打击(更少的缓存,更少的资源......)
那么,自 Win7 以来,WH_CALLWNDPROC 挂钩是否变得如此缓慢? 快速谷歌搜索似乎没有发现任何其他关于此问题的报告。有人可以重现我的结果吗? 如果它不能被复制,任何人都知道什么设置/冲突的应用程序可能会导致这种情况? (已经尝试禁用 Windows Defender 实时扫描,不影响性能)。
编辑:这是在 Win10 1803 64 位下测试的。测试应用程序本身是 32 位的。
EDIT2:以 64 位编译的相同应用程序给出以下执行时间。
- Win10:780 毫秒/10201 毫秒。
- Win7:6419 毫秒/9201 毫秒。
EDIT3:有趣的是,当以管理员身份运行应用程序(32 位)时:
- Win10:12693 毫秒/18028 毫秒
另外,(在另一个工作站上),以不同的用户身份运行会有所不同:
- Win10(1809) / “标准用户”:9430 / 17440 毫秒
- Win10(1809) / 系统:5220 / 10160 毫秒(通过 PsExec 远程启动)
EDIT4:如果以管理员身份运行,应用程序从 USB 密钥运行的速度将比从硬盘运行得快。 (注:到目前为止,我只在具有单个驱动器的系统上进行了测试。在这一点上,我不排除只有操作系统驱动器较慢。)
EDIT5:我发现了很多关于这种情况的事情。 首先,运行“As Admin”(win10) 会导致应用程序安装 WH_CALLWNDPROCRET 挂钩。我还没有找到它来自哪里(操作系统、Delphi 的框架、其他应用程序?)。只是运行应用程序时肯定不存在。
对性能的影响似乎并不在于钩子本身,而是在于它对 SendMessage 的影响。
我们正在联系微软的支持,他们已经重现了类似的结果(在 100k 循环而不是 1m 上):
- Windows 7 - 无挂钩 0.018396 秒。
- Windows 10 - 无挂钩 0.025102 秒。
- Windows 7 - 带挂钩 0.167941 秒。
- Windows 10 - 使用钩子 1.105929 秒。
(调查仍在进行中,目前还没有结论)
这些结果还表明,我们的许多工作站的性能都比不涉及挂钩时的性能要差。
【问题讨论】:
-
Win10 通常有更多的开销(控制流保护等),但开销不应该那么大。你知道你的系统是否正在运行其他带有钩子的应用程序吗?也许是调试钩子?
-
@Anders 据我所知,没有涉及其他钩子。我安装了自己的全局调试钩子,我收到的只是我自己的 WH_CALLWNDPROC 钩子的通知。现在,调试钩子不会收到调试钩子的通知,除非我没有正确测试它......)。我还在 CallNextHookEx 中设置了一个断点,对它的每个调用都来自我自己的钩子。不过,老实说,我对全局钩子了解得不够多,不知道这是否足够证据。
-
@KenBourassa 我不知道 Windows 10 上的 WH_CALLWNDPROC 挂钩有任何重大的性能问题。我的问题是 Delphi 代码中的 Combobox1.ItemIndex 在发送到窗口消息方面实际上做了什么组合框控件。
-
Windows 7 和 Windows 10 之间的一个区别是,调用钩子链中的下一个钩子现在涉及回调到内核模式以获取有关钩子链中的下一个钩子的信息。除非内核方面存在一些锁争用,否则我不知道有任何问题会导致执行应用程序代码所需的时间出现如此巨大的差异。
-
Spy++ 使用 WH_CALLWNDPROC 挂钩来监控发送的窗口消息。如果您使用 Spy++ 来监视发送到您的应用程序拥有的窗口的窗口消息,如果您看到类似的延迟,那将是值得的。
标签: winapi windows-10