Performance issue under 64 bit with 4.0.12

delphi package - automated exception handling
Post Reply
davidheffernan
Posts: 89
Joined: Thu Feb 23, 2012 12:22 pm

Performance issue under 64 bit with 4.0.12

Post by davidheffernan »

Hi,

I've just discovered a rather strange performance issue in my Excel COM add-in. The issue arose when I took the upgrade to version 4.0.12 having previously used 4.0.11. I'm compiling under XE7, 64 bit, and the troublesome module is an Excel COM add-in.

It's pretty tricky to reproduce, which I know is frustrating. Perhaps you could help me understand what the madExcept code is doing to help me narrow down my search. I rather imagine that my code is raising a lot of handled exceptions, but I don't at the moment see why it would, nor am I able to run under the debugger to inspect because the 64 bit debugger is, er, unreliable. I'm not able to get any debug symbols. What debugging I have done suggests that my code is raising handled exceptions, and I will try to understand why.

What is interesting is that I looked at the changes introduced in 4.0.12 and I found that I could resolve the performance issue by making a change here:

In 4.0.12 IsClass is implemented like this:

Code: Select all

function IsClass(obj: TObject; const className: AnsiString) : boolean;
var cn   : AnsiString;
    inot : boolean;
begin
  result := IsValidObjectEx(obj, cn, inot, className);
end;
In 4.0.11 it is:

Code: Select all

function IsClass(obj: TObject; const className: string) : boolean;
var clss : TClass;
begin
  result := false;
  try
    if obj <> nil then begin
      clss := obj.ClassType;
      while (clss <> nil) and (clss.ClassName <> className) do
        clss := clss.ClassParent;
      result := clss <> nil;
    end else
      result := false;
  except end;
end;
If I revert to the old, then the performance issue disappears. Indeed, if I call the old IsClass from IsValidException that is enough also to resolve the issue.

As of now I'm going to try to understand why my code is raising lots of handled exceptions and see if I can reduce the number of them. However, it does feel as though the 64 bit performance is impacted by handled exceptions from 4.0.12.

From my perspective, with my incomplete knowledge, I prefer the implementation of IsClass from 4.0.11! ;-)
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Performance issue under 64 bit with 4.0.12

Post by madshi »

Ah well. I made the change because madExcept sometimes has to handle exception objects raised by a different module (DLL/EXE) which might have been compiled with a different Delphi version. If that's the case, the exception object might have a different fundamental object structure compared to your own Delphi version. The old "IsClass()" method didn't handle this situation correctly, so "IsClass()" either crashed or reported false for such situations, while the new code handles it correctly.

I'm planning to work on this problem for the next build, though. See also these threads:

viewtopic.php?f=4&t=28002&p=48774#p48774
viewtopic.php?f=4&t=27307&p=48749#p48749
davidheffernan
Posts: 89
Joined: Thu Feb 23, 2012 12:22 pm

Re: Performance issue under 64 bit with 4.0.12

Post by davidheffernan »

Thanks for the response. I can confirm that my particular issue was related to a part of code that did standard control flow using exceptions. Indeed, I had even commented that this was not ideal:

Code: Select all

    Try
      CheckObjectExtra(ObjectHandle, MyResultType, OrderedVarID, ObjectExtra);
      ObjectExtraValidForAtLeastOneVar := True;
      CheckVarID(ObjectHandle, ObjectExtra, MyResultType, OrderedVarID);
    Except
      on EAPIError do begin
        // If ObjectExtra or VarID are not valid then CheckObjectExtra or
        // CheckVarID will throw an exception, in which case we move onto
        // the next VarID. Ideally, exceptions should not be used in this way.
        continue;
      end;
    End;
So, I took my own advice and replaced the two CheckXXX methods with TryCheckXXX and resolved this particular issue.

What concerns me slightly is if there are other similar blocks of code that will cause perf hot spots. As you can imagine, I have a somewhat sprawling app and it's plausible that it will be my clients rather than myself that uncover the hot spots.

I'd really appreciate it if you were able to address the performance issue with IsClass in some future release.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Performance issue under 64 bit with 4.0.12

Post by madshi »

Well, I'll certainly try.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Performance issue under 64 bit with 4.0.12

Post by madshi »

Does this beta build solves the performance problem for you?

http://madshi.net/madCollectionBeta.exe (2.7.11.6)

I've *not* changed IsClass, though. Instead I've generally optimized the handling of "handled" exceptions (those which madExcept never fully does anything about because some try..except block already took care of them).
davidheffernan
Posts: 89
Joined: Thu Feb 23, 2012 12:22 pm

Re: Performance issue under 64 bit with 4.0.12

Post by davidheffernan »

Sorry, I did not get a notification of this post. Which is why I have not responded.

I'll take a look as soon as I can, and get back to you.

Thank you very much.
zunzster
Posts: 58
Joined: Wed Oct 29, 2008 3:43 am

Re: Performance issue under 64 bit with 4.0.12

Post by zunzster »

OK. It's all good now and in fact apparently faster with 'hidden' 64-bit exceptions that 4.0.12 is in our tests.

It's definitely the IsBadReadPtr cs. VirtualQuery change in IsValidObjectEx that makes a noticeable difference.

However, I did end up making the following change to HandleCreateThread since we were getting intermittent but subsequently swallowed Access Violations from IsBadReadPtr (of course - that's how IsBadReadPtr works) firing when running in the Delphi IDE (i.e. first chance debugger exceptions as you'd expect) when non-TThread threads were created by TAPI and other APIs we interact with that create 'naked' Win32 threads in your process. Maybe there is a way to make the Delphi Debugger not fire just for IsBadReadPtr checks (as opposed to other AVs) which would mean this wasn't needed.

Code: Select all

    function IsValidAddress(p: pointer): Boolean;
    var
      mbi : TMemoryBasicInformation;
    begin
      Result := (VirtualQuery(p, mbi, sizeOf(mbi)) = sizeOf(mbi)) and
       (mbi.State = MEM_COMMIT) and
       (mbi.Protect and (PAGE_EXECUTE_READ or PAGE_READONLY or PAGE_READWRITE or PAGE_WRITECOPY) <> 0) and
       (mbi.Protect and PAGE_GUARD = 0);
    end;

inside HandleCreateThread
..
    if (param <> nil) and
       TryRead(@TPThreadInfo(param)^.param, @p1, sizeOf(p1)) and
-->     IsValidAddress(p1) and <--
       IsValidObjectEx(TObject(p1), classA, newObj, 'TThread') then begin
..
This makes sense in that using the x86 page tables to trigger AVs to check an address vs. examining the OS page tables in x86 code is a whole lot more instructions and thus an order of magnitude more time.

Anyway, thanks for your attention to this Mathias.

Cheers,
Paul.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Performance issue under 64 bit with 4.0.12

Post by madshi »

Yeah, just yesterday I stumbled over the same issue. IsBadReadPtr does have its problems, that's why I didn't use it originally.
davidheffernan
Posts: 89
Joined: Thu Feb 23, 2012 12:22 pm

Re: Performance issue under 64 bit with 4.0.12

Post by davidheffernan »

Mathias,

I can confirm that the code from installer version 2.7.11.6 resolves the original issue that I reported. Thanks. I'll wait for an official release which is not doubt not too far away given that there's been (yet) another Delphi release since the last official madExcept.

Thanks again for your excellent work and support.
Post Reply