1

We have have a block of code that generates a lot of exceptions (IndexOutOfRangeException apparently). It's possible that it generates up to 50,000/sec. They are caught but the cpu really spikes. The code block simply checks an IDataRecord for a column. The block is very generic though and if the column isn't there, the exception is caught and returns a boolean.

If it weren't for the number of exceptions this wouldn't be an issue. The only way I can think to fix this is to iterate through the columns of the IDataRecord to see if the column is there before processing, but this also seems like it could be an expensive step because this is a very high traffic application and you would have this loop before accessing any column.

I'm just looking for some thoughts.

CYAD
  • 985
  • 2
  • 14
  • 21
  • 25
    Fix the code. `IndexOutOfRangeException` is one of those exceptions you should never see! – leppie Jan 24 '12 at 16:11
  • 6
    Dealing with the Exceptions is not the right approach. I'd recommend concentrating on why these exceptions are being thrown. Exception handling is not free and with that volume not lightweight at all. This type of exception is almost always avoidable. – Chris Ballance Jan 24 '12 at 16:12
  • 24
    First off, it is usually hundreds of times cheaper to prevent an exception than to handle it. Second, accessing a non-existing index is a *bug*. The program logic is simply wrong. Don't continue to wallpaper over the gaping hole; fix the hole. – Eric Lippert Jan 24 '12 at 16:13
  • @EricLippert: What if the index being sought and the thing to be searched in are determined by an external data source (e.g. a file being deserialized)? If one is deserializing a file, one could check an index read from a file before attempting to use it, but if the result of an invalid index would be to abort deserialization, is anything really gained by adding more work to the 'common' case? To be sure, if the out-of-bounds index is a common case (as with the OP's question), then it makes sense to check it, but when an erroneous condition should cause an exception, why not let it? – supercat Jan 24 '12 at 16:29
  • 6
    @supercat: That's a good example. This is a realistic user scenario and therefore should be in the specification of the subsystem of the program that deals with deserialization. Were I writing the spec for such a subsystem, my spec would probably say *Users may provide files that are "corrupted" in some way. If the file is corrupted then the deserialization subsystem will detect the corrupted file and throw a custom "corrupt file" exception.* **Throwing "bad index" and expecting the caller to know that it means "corrupt file" is very bad design.** – Eric Lippert Jan 24 '12 at 16:59
  • @EricLippert: Allowing an `IndexOutOfRangeException` to cross application layers would probably be bad, but I don't see much advantage to checking subscripts before using them rather than catching and wrapping `IndexOutOfRangeException`, given that it would clutter and slow down the "success" case. – supercat Jan 24 '12 at 17:37
  • @supercat: But then how do you know that the exception you are catching is an exception *because the file is corrupt* or an exception you are catching *because your program has a bug in it somewhere*? Catching that exception *hides your bugs*. – Eric Lippert Jan 24 '12 at 17:50
  • @EricLippert: That is, to be sure, one of the limitations with the way exceptions are coded and implemented. It's too bad there's no nice easy shorthand for saying "if an exception occurs in this statement, use some particular exception mapper" without either cluttering up the code with an absurd number of try/catch blocks or slowing down the main-line case. – supercat Jan 24 '12 at 17:57

3 Answers3

3

Make sure the columns you're looking for exist first. Not only is throwing exceptions usually an even bigger hit performance-wise, but all those spurious exceptions might be hiding legitimate bugs in the code.

The best way to make sure the columns exist first is to bring all those IDataRecord classes in line with this function's assumptions about what kind of data they contain. Or bring the function in line with the actual data. Having a procedure assume that a value exists when it doesn't implies that your program's structure is not a good match for the business logic it implements, and that's a situation that shouldn't be allowed to persist.

Second up would be to loop through each object's fields to see what it actually contains that field, but this smells of sweeping a deeper problem under the carpet.

On no account, though, should you be relying on exceptions for control flow.

Sean U
  • 6,730
  • 1
  • 24
  • 43
  • Agreed. I have no control over the calling code though so clean up on that end is not an option. It seems like the consensus is to make sure the column exists before execution. I'll try that and see how expensive it is. – CYAD Jan 24 '12 at 16:58
  • So what's the best way to check if a column exists? Can you provide a code example for what is described in the second paragraph? – MrBoJangles Feb 28 '13 at 17:23
0

If you are checking the columns through the index. You should avoid the exception first. Use the IsDBNull Method of the IDataRecord instead, more information here

And Yes you should check first if a column exists, that's why you are getting the IndexOutOfRangeException

Hector Sanchez
  • 2,297
  • 4
  • 26
  • 39
0

"The only way I can think to fix this is to iterate through the columns of the IDataRecord to see if the column is there before processing"

This error only occurs if you're beyond the end of the array. You don't need to step through each column, just get the current column "count"

for(int i = 0; i < column.count; i++)
{
dowork(column[1]);
}

From what I've read, an exception costs about 5,000-10,000 clock cycles. Iterating through the columns would be much cheaper in most cases, but I'm sure the properties for the object will let you know how many columns exist without actually counting them every time.

Bengie
  • 1,035
  • 5
  • 10
  • We're accessing the column by name, not index. – CYAD Jan 24 '12 at 19:52
  • http://stackoverflow.com/questions/373230/check-for-column-name-in-a-sqldatareader-object "r.GetSchemaTable().Columns.Contains(field)" – Bengie Jan 24 '12 at 21:08