7

I'm trying to both learn more about the implications of various design choices, and find the most performant solution for preventing errors in my code. Before diving in, allow me to briefly and generally state my question:

Is a try catch block a more efficient solution to handling an out of bounds exception when compared to and if-statement structure?

I understand that raising exceptions is expensive, but is that cost mitigated by removing unnecessary if-statements?

And now, allow me to state my specific issue to give you more useful information.

I'm building a game, and part of that game is a world-grid solution coupled with a pathfinder. Game Units may request nodes from the world-grid, (by sending coordinate information as an ordered pair (x, z)), to then send to the pathfinder or perform on them some other miscellaneous action. Because nodes are being requested with a large amount of frequency, especially by the pathfinder, this seems like a smart place to try to optimize.

Here is my current code for returning a given node based on its x and z values in the array of nodes:

public Node GetTile(int x, int z)
{
    if (x < 0)
        x = 0;
    if (x >= length_X)
        x = length_X - 1;

    if (z < 0)
        z = 0;
    if (z >= length_Z)
        z = length_Z - 1;
    return tiles [x, z];
}

As you can plainly see, I have several if statements in place to avoid out of bounds exceptions when retrieving a node from world-grid's array of nodes(tiles). Is this the most efficient solution? Could I remove the if's and instead place the return in a try catch block like so:

public Node GetTile(int x, int z) 
{
    try
    {
        return tiles[x, z];
    }
    catch (System.IndexOutOfRangeException e)
    {
        //handle exception here
        //do smart programming fix-y stuff
    }
}

This solution works, but is there some reason that I'm not aware of that makes it less efficient? My logic for believing it to be a smarter solution is that I'm removing up to four comparisons every single time the method is called. Considering the circumstances under which the program will actually send in values out side of the bounds are rare, this seems like a good trade off. What I don't know though, is how exceptions work behind the scenes. Will checking for an exception be just as expensive as checking the values manually?

UPDATE:

After reading through the replies here I now have a better understanding of the topic in general. I've run some tests on my code accounting for various cases running 10 million iterations.

In line with my general hypothesis, if we assume that there will never be a case where input results in an exception, then the try catch block is indeed faster. However, in the case where approximately 10% the calls to the method raise an exception, the performance is over 5 times slower. I tested various percentages and it seems that the threshold for better performance in my case is near 1%.

As of yet, I'm not sure what percentage of calls will indeed actually raise an exception, but I believe that the number will be much much lower than 1%. Therefore, I can safely, unless further testing shows otherwise, use the try-catch block to ensure my code runs without fatal errors.

Thanks to everyone who contributed.

Caboose
  • 453
  • 5
  • 16
  • 1
    Benchmark it. Run the code 10,000 times and see which performs better. However, exception handling **is** expensive, so if the input it likely to be out of range go for the if tests. – ChrisF Sep 26 '16 at 19:36
  • 2
    Exceptions will always be more costly than if statements and there are a number of questions here on Stack Overflow that show as much, not to mention other interesting things such as changes in the call stack. You also could have profiled it yourself to determine if there is a cost difference in your case. – David L Sep 26 '16 at 19:37
  • 4
    `if` and exceptions are for different things. Exceptions are not for control flow. Exceptions are for *exceptional* circumstances which you don't usually need to handle, but that you know may arise. Thus, out of bounds *should* be an exception. All of your `if` statements are probably masking a logic error elsewhere, which is what exceptions and logging help uncover. – Glorin Oakenfoot Sep 26 '16 at 19:38
  • You must use exceptions only for exceptional cases. I know this is the default response when a question how to use exceptions arises, but there is a good reason to it. If your code path offers the opportunity to evaluate conditions before an error occurs, it is best to do so. Abusing exception handling makes your code hard to follow and prevents optimised compilation. –  Sep 26 '16 at 19:39
  • Not a great duplicate choice. I think http://stackoverflow.com/questions/18648987/try-catch-best-practices-for-optimal-performance might have been a bit more spot on. – David L Sep 26 '16 at 19:41
  • @GlorinOakenfoot yes thank you, that is effectively what I was asking. The way the program is designed allows for out of bounds values to be sent in, but it also is structured in various ways to make it unlikely. – Caboose Sep 26 '16 at 19:42

1 Answers1

11

Exceptions are not made to control the flow of your code. They are made to treat "exceptional" circumstances, e.g. connection issues or invalid file formats.

If you want to control your code flow, you should use conditional statements like if, else and switch.

The performance of exceptions is generally bad compared to some simple if-statement, but there are also situations when exception-handling might be even faster than checking data for validity manually, especially if you don't know how to do it fast.

There might also be plenty of situations where you "know" an exception could be raised, but you simply trust the data - e.g. checking your own settings file for valid xml format. Data validation is paid in CPU-time.

Most of the time, you want to use Exceptions, to have error information later, e.g. line number of the error in the xml file and to recover your application in some way (e.g. reconnect to a socket).

You can not give a general statement about the performance of exceptions, but when you can avoid them, you should. They are made for exceptions as their name already tells.

A small example where you can potentially avoid exceptions:

try
{
    LoginUser("Fooooo", "Bar");
} catch(InvalidPasswordException e) {
    MessageBox.Show(e.Message);
}  

private static void LoginUser(string pUsername, string pPassword)
{
    if (pUsername != "Foo" || pPassword != "Bar")
       throw new InvalidPasswordException("Invalid username and/or password");
    
    GrantAccess(pUsername);
}

Instead of throwing the exception you could also just return a boolean:

if (!LoginUser("Fooooo", "Bar"))
{
    MessageBox.Show("Invalid username and/or password");
}

private static bool LoginUser(string pUsername, string pPassword)
{
    if (pUsername != "Foo" || pPassword != "Bar")
        return false;
        
    GrantAccess(pUsername);            
    return true;
} 

Performance-Test: 10000 attempts with false credentials:

throwing Exception (without displaying the messagebox): 181990454 ticks

returning boolean: 644 ticks

And we are only 1 layer into the application, so there is not much stacktrace to be generated. I guess you can see and especially feel the difference.

Dominik
  • 1,623
  • 11
  • 27
  • Yes, I've read as much, and perhaps I wasn't clear in the post, in my case the exceptions should definitely be very uncommon, so the overhead of generating the stacktrace will very rarely come up, yes? – Caboose Sep 26 '16 at 19:39
  • 3
    I did not downvote, but I suspect it is because you are making blanket statements without any quantifiable evidence. This answer would be far more useful if you used test code to profile a difference in a way that was quantifiable, as well as demonstrated the differences between the stack traces. – David L Sep 26 '16 at 19:43
  • 1
    Agreed with @DavidL. Would be nice to see either example code, or links to *something* that prove the claim. However, I would not say this answer is "not useful" (the downvote reason), so a downvote was probably a tad harsh. – Broots Waymb Sep 26 '16 at 19:52
  • Thanks for the edited response, this is much more clear. After reading through your new answer and all the previous commenting I feel that I have a much better understanding of the issue now. – Caboose Sep 26 '16 at 21:33