Cost of Try ... Except

Hi
There have been a number of comments recently about Exceptions being very expensive in terms of performance.

I had assumed that Try Except would have a similar cost to Try Finally except in the case when an exception occurs and the exception parameters need to be marshaled.

It that a naive assumption and has anybody done tests or seen studies on the impact of exceptions.

I think the only time you really need to worry about it is where you are inside a tight loop - for the most part the overhead is much less than our own code.

So this will be slower

for i := 0 to list.count -1 do
begin
  try
   ...
  except
   ...
  end;
end;

Than this

try
  for i := 0 to list.count -1 do
  begin
  ....
  end
except
 ...
end

If you have enough items to iterate.

Disclaimer - not an expert… just my experience. If performance really matters, profile your code.

@RogerConnell, here was where I tested out try … except with a simple division example.

I assume if you don’t catch an exception in your code it bubbles out and terminates the show? or am I just typing tired?

In any case, maybe it might be something to play with.

My understanding is that Win32, try-finally and try-except are really expensive. In Win64, try-except is a lot less expensive, as MS designed them better.

But like Vincent: Disclaimer - not an expert… just my experience. If performance really matters, profile your code.

Testing Delphi Try Except

Testing the impact of Try Except End in Delphi Across Platforms.

Background

@Paul_McGee provided a GitHub example program to measure the impact of Try Except. This program however tests the “Cost” of raising the exception while what worried me was the impact on performance of including the “catch”.

I use try - excepts as a means of isolating and understanding problems in systems often placing a break point in the on except code. It has been my practice to leave these catches in the code for future reference.

        if (d <> result) then
          try
            result := d
          Except
            On E: Exception Do
            Begin
              raise Exception.Create('Error in result<>d ' + E.message);
            End;
          End

I have taken Paul’s approach and packaged it in Firemonkey so that it can be run on other platforms. I then included a comparison between code with an insane amount of try-excepts with the same code unhindered but in a test where no exceptions are raised

Conclusion

My minimal testing yields

Performance penalty of inserting Try Except Blocks (at an insane rate) Android 25% Windows 32 70% Windows 64 50% which is not significant given the potential diagnostic return.

Other Interesting Stats

Windows 64 is 15% slower on this test than Windows 32

Android 32 takes 11 times longer than Windows 32 on the hardware platforms used.

While Raising Exceptions on Windows 32 is very expensive it is marginally less so on Windows 64 and almost no impact on Android 32.

1 Like

@RogerConnell Unfortunately your assessment and conclusions are wrong - the main performance impact you are seeing results from cleanup code the compiler inserts for the string operations for the exception text. Even though the exceptions are never happening the additional code still gets executed (implicit try finally code).

See Code Optimization: Go For the Jugular - DelphiTools

Putting all raise line into a sub routine like so:

  procedure Raise1(E: Exception);
  begin
    raise Exception.Create('Error in divexcept ' + E.message);
  end;

reduces the cost to around 10-20% for Win32/Win64 - did not test other platforms as these compilers produce garbage binary code all across the board anyway.^

<insert rant about poor optimization on Win64 and completely garbage register allocation> - I am too lazy to look up the various QP tickets for that

This is by the way the reason why try/except is not no cost on win64 as it should be given the way exceptions work in win64. Instead of simply moving stuff around in a few registers the generated code moves it back and forth from/to the stack

1 Like

Your comment on the impact of the compiler string handling is interesting and I have taken note however I am very comfortable with my conclusion that the performance penalty of inserting Try Except Blocks (at an insane rate) is not significant given the potential diagnostic return.

The question was never about obtaining optimised performance but ensuring that my habit of leaving diagnostic hints in Try Except blocks was not too stupid.

It is much easier to search for a phrase in my extensive code library directories to find troublesome code than trying to set up a Debug test bed version and replicate the error.

With respect to the Win 32/64 debate I simply noted the fact that Windows 64 is 15% slower on this test than Windows 32. Possibly because in THIS test the compiled Exe size is larger in 64bit. On reflection this could be expected.

In responding to this question however I did rerun the tests using the exe files outside the IDE and observed results that suggest that the original claim that exceptions are expensive is a furphy and the perceived poor performance comes from the IDE being required to handle any exceptions raised.

Code running in IDE

Total 10000000  Num_OK=9998000  Num Div Zero=2000
time: 79 ms

Throw exceptions - 1 in 5000
Total 10000000  Num_OK=9998000  Num Div Zero=2000
time: 6624 ms

Code Running Directly

Total 10000000  Num_OK=9998000  Num Div Zero=2000
time: 37 ms

Throw exceptions - 1 in 5000
Total 10000000  Num_OK=9998000  Num Div Zero=2000
time: 45 ms
1 Like

It’s a very specialised area running at the real pointy-end of performance, so it’s unlikely to matter unless you need to squeeze as much out of every clock cycle as you can. In 3 decades of software development I only had the need for this once, 6 months ago. In that case I increased performance significantly by ensuring all data used was contiguous and in the L1 cache :wink: Having said that it is an interesting area to read about

This link is old and for C#, but does a nice analysis that show some general areas to look it if this sort of thing floats your boat:

https://www.codeproject.com/Articles/146797/Fast-and-Less-Fast-Loops-in-C

1 Like

:slight_smile:

Not quite but the topic is called “Cost of Try … Except” and you wrote:

which I commented on telling you that the cost you measured includes the additional string handling and not only stems from the try except blocks which only strengthened your statement:

I just wanted to point that out before people keep spreading “try except costs >=50% performance” somewhere. :wink:

To be honest I never expect anyone to run any benchmarking code under the debugger :wink:

Executable size has nothing to do with performance in this case - it’s about the code being generated by the compiler. Sizes of the executables have different reasons - iirc the linker for dcc64 is not as smart as for dcc32 when it comes to removing unused code.