Ken Bourassa
Ken Bourassa

Reputation: 6467

Did WH_CALLWNDPROC hooks performance dramatically decrease with Win10 (Compared to Win7)?

We are in the process of upgrading our workstations to Win10 from Win7. While investigating reports of performance degradation, I came to the conclusion it was caused by a WH_CALLWNDPROC hook installed by a third party.

I came to this conclusion based on the result of the following test application (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;

(For those unfamiliar with Delphi, TStopwatch uses QueryPerformanceFrequency/QueryPerformanceCounter APIs to get elapsed time)

The execution time for this method is

(Note : Both machine are on wildly different hardware and can't really be compared to eachother).

Now, if I add a hook before executing the same code

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;

The execution time now becomes :

Now, as I mentionned, both workstation are on different hardware, but I don't believe that alone could explain the difference. Win10 has an i7 cpu while Win7 has an i3. If anything, I'd expect the i3 to take a bigger hit (less cache, less resource... )

So, did WH_CALLWNDPROC hooks get that much slower since Win7? A quick google search didn't seem to reveal any other report of this issue. Can anybody reproduce my results? If it can't be reproduced, anyone has any idea what settings/conflicting application could be causing this? (Already tried disabling Windows Defender real time scanning and it didn't affect performance).

EDIT : This was tested under Win10 1803 64 bits. The test application itself was 32 bits.

EDIT2 : Same application compiled in 64 bits gives the following execution time.

EDIT3 : Interestingly enough, when running the application (32bits) as admin :

Also, (on yet another workstation), running as different user makes a difference :

EDIT4 : If ran as admin, the application will run faster from a USB key than from a hard disk. (Note : So far, I only tested on system with a single drive. At this point, I wouldn't exclude that only the OS drive is slower.)

EDIT5 : I found out quite a few more things about this situation. First, running "As Admin"(win10) causes the application to have a WH_CALLWNDPROCRET hook to be installed. I haven't found where it is coming from (OS, Delphi's framework, other app?). It is definitely not there when simply running the app.

The performance hit doesn't seem to be so much on the hook itself, but on its effect on SendMessage.

We are in contact with Microsoft's support, they have reproduced similar results (on a 100k loop instead of 1m) :

(Investigation still on-going so still no conclusions thus far)

Those result also suggest many of our workstations perform way worse than they should when there are no hooks involved.

Upvotes: 4

Views: 897

Answers (1)

Ken Bourassa
Ken Bourassa

Reputation: 6467

So, WH_CALLWNDPROC and WH_CALLWNDPROCRET hooks do degrade performance quite a bit. And quite a bit more so in Win10 than it did in Win7.

Some of the performance hit is coming from the mitigation code for Spectre and Meltdown. Early reports from Microsoft suggest the rest is apparently from lock contention in the window manager (win32k*.sys).

As for the weird result I've got in my investigation :

  • Running "As Admin" caused an additional hook to be installed in my application which explains the massive slowdown I witnessed
  • Many of the test I did were in test machine accessed through a remote admin tool... which happens to install global WH_CALLWNDPROC/WH_CALLWNDPROCRET hook itself, which made my test result flawed. Running locally "fixed" the results. Took me a while to find out about it since my application is 32 bits and the hooks were 64 bits, so my application wasn't notified of them (but still incurred the performance hit).

2020-02-04 : I just received an update from Microsoft. Their engineer identified a few issues that contribute to the performance degradation. Current estimate for a Windows Insider version containing fixes is 2020H1, early 2020H2

Upvotes: 3

Related Questions