After writing my previous blog post on the performance gains offered by the Virtual File System in ColdFusion 9, I started to think about Elliott Sprehn's recent blog post on using ARGUMENTS.Callee in ColdFusion. In it, Elliott uses manually-triggered exceptions to gain access to an instance of the Function object from within the function body. Any time that someone offers a solution that leverages manually-triggered exceptions, someone always comments that Exceptions are too expensive (rendering the solutions nice, but not usable).
I have always accepted the fact that Exceptions are costly, but I never really had a sense of just how expensive. As such, I decided to run some simple tests to see what kind of performance hit there really would be:
<cffunction name="doSomething" access="public" returntype="array" output="false" hint="I append a value to an array with the option to throw and catch an error."> <!--- Define arguments. ---> <cfargument name="data" type="array" required="true" hint="I am the array to which we are appending a value." /> <cfargument name="value" type="any" required="true" hint="I am the value being appended to the array." /> <cfargument name="throwException" type="boolean" required="true" hint="I determine if we are going to throw an exception." /> <!--- Append the given value to the array. ---> <cfset arrayAppend( arguments.data, arguments.value ) /> <!--- Check to see if we need to trigger an exception. ---> <cfif arguments.throwException> <cftry> <!--- Throw manual exception. ---> <cfthrow type="manualException" /> <cfcatch> <!--- Nothing to do here. ---> </cfcatch> </cftry> </cfif> <!--- Return the updated array. ---> <cfreturn arguments.data /> </cffunction> <!--- ----------------------------------------------------- ---> <!--- ----------------------------------------------------- ---> <!--- ----------------------------------------------------- ---> <!--- Loop over the true/false boolean flag for triggernig an exception in the target method. ---> <cfloop index="throwException" list="false,true" delimiters=","> <p> Throw Exception: #throwException# </p> <!--- Get start tick count. ---> <cfset startTickCount = getTickCount() /> <!--- Create a new array. ---> <cfset data =  /> <!--- Call the method many times to test performance. ---> <cfloop index="iteration" from="1" to="1000" step="1"> <!--- Call the method and store the result back into our data variable (so that it grows over time). We will be using the outer loop to manage the exceptions. ---> <cfset data = doSomething( data, iteration, throwException ) /> </cfloop> <!--- Get the end tick count. ---> <cfset endTickCount = getTickCount() /> <p> Results: #numberFormat( (endTickCount - startTickCount), "," )# </p> </cfloop>
As you can see in the code, I have create a test function, DoSomething(), which builds an array of data each time it is called. In addition to the array and value arguments, it also takes a boolean flag as to whether it should throw and catch an Exception internally. In the test code, I then loop over each boolean flag and call the DoSomething() method 1,000 times. These are the results that I found:
Throw Exception: False
- Results: 156
- Results: 141
- Results: 125
- Results: 141
- Results: 125
- Average: 137
Throw Exception: True
- Results: 1,875
- Results: 1,859
- Results: 1,859
- Results: 1,844
- Results: 1,859
- Average: 1,859
Ok, so the tests that triggered exceptions ran 13 times slower overall. This is definitely a significant cost of execution. So, I guess the real question comes down to use cases. If you are going to be using this technique all the time on a high load site, then yeah, maybe it's not the best idea. But, if you're going to be using it to solve a problem that is not too common, then I'd say it is still quite reasonable.
Want to use code from this post? Check out the license.
Heh, thanks for this. I fight this battle, primarily with OO-purists, enough that I end up coding up something like this about once each year. Now I can just point and say "Ben said so, and he's too nice to lie to you".
Exceptions are expensive. They are supposed to be expensive, because they are supposed to be rare. That's why they're called "exceptions".
There's nothing wrong with procedural/classic error handling -- especially if the errors are common, and not exceptional.
This is a real eye opener. I had no idea throwing exceptions was that expensive. So is it considered better practice to wrap functions calls within try/catch blocks like this
as opposed to putting a try/catch block inside all non-trivial methods of a CFC?
I'd argue that exceptions should truly be reserved for "holy cow, I don't have a clue how to handle this" types of situations. They should not be used for easy-to-handle problems like missing attributes or basic error checking. It's a judgment call, and there aren't any hard and fast rules.
Some very contrived examples:
1. You're in the middle of a bunch of queries and suddenly lose your db connection. (In which case you probably want to rollback before throwing.)
2. You run out of space while writing to a filesystem.
To more directly answer your question, think about it this way: you want your catch block to be able to handle the situation. If it can't figure out how to gracefully recover from the problem, it shouldn't be trying to catch in the first place.
In any of the contrived examples above, for instance, if you put the catch block too high up, it's not going to know how to clean up from a disconnected db because it knows nothing of the transaction that was going on. Or, it's not going to know how to handle a disk space problem.
Logically, then, your catch blocks should almost always be right next to the code that could cause the problem, because the cleanup should be intimately tied to the disaster that caused it.
Now, that cleanup code might do its best but ultimately decide that even it can't recover gracefully, at which point it should re-throw up the chain. The higher blocks aren't going to be able to correctly clean up the problem, but maybe they still have a chance of preventing further, related damage. That chain of throws is going to be slow, but that's okay because crashing doesn't need to be fast or optimized. But, if the original catch block can recover gracefully then re-throwing is less desirable than just returning an error code -- the equivalent of saying "I had a problem, but I cleaned it up and it's no longer critical".
Long story short: you shouldn't try to catch unless you know how to clean up the mess, and even then you should be trying to catch very specific problems, not just anything and everything. And you shouldn't throw for just anything, you should throw because you've crashed too hard to know what to do.
When people throw exceptions for non-error purposes, it's usually to get at the tag context or the stack info. It'd be great if there was an easy API to access that stuff without having to throw exceptions.
"It'd be great if there was an easy API to access that stuff without having to throw exceptions."
There is. java.lang.Exception.
I remodelled your code to test the <cfthrow> approach compared to creating an exception using Java, with a control of neither, and the results were along these lines:
Throw Exception: throw
Throw Exception: create
Throw Exception: none
Fascinating!! I'll have to do some playing around with that! Thank you for the tip.
Ben, thanks for the info! Very interesting -- I figured they were "expensive" but to see actual numbers/stats is quite enlightening.
Jose, the rule of thumb I usually use is, "if it's NOT physically possible to write an if/else statement to check this error, then use a try/catch block" (i.e. the example above about losing your db connection halfway thru a bunch of queries -- there's no way to write "cfif the database goes down...", so I'd wrap that in a try/catch. Whereas things like missing arguments are easily done with cfif and StructKeyExists().
my question is just by using the cftry tag will it will it cost me an extra overhead?
Let say I have 10 queries on a page which will be executed one at a time, although 10 is too much but for the sake of example just consider it. Which page will load faster? the page that has 10 cftry catch for each query or the page that don't have any ctry on each query? Or they will load at same speed. The purpose of the ctry is to anticipate database connectivity. Consider also that when the page load there will be no errors to be thrown.
Let give my opinion to my question above.
I think there's no difference at all. My concern was just by using ctry, java objects on the background already loaded the System.Exception or what ever class it belong to and with that alone it already cost the server some resources but it occurs to me that whether or not we use cftry that Exception Class will always be loaded.
The reason why I said that because of the methods we can use inside the Application.cfc, namely: OnError()and OnMissingTemplate. Both method without a doubt uses exception class.
cftry shouldn't make more overhead. It just helps prevent errors from displaying, and can help with better error management.
Also note that coldfusion has a function called onError which would save you the time from having to go into your code and add all the cftry and cfcatch tags.
You have to consider why you have CFTry / CFCatch blocks. You only need them when you have errors that you *can* recover from in some way. If you have queries that fail, that seems pretty catastrophic. What error would you prevent to the user? "I'm sorry, your form could not be processed because my database server has died?"
You only need CFTry / CFCatch when you can do something useful in the CFCatch block. If you can't, just let the code die and have the error be handled at the site-wide error handler such as the OnError() event handler in Application.cfc.
Do you know if there is a way to get the tag context without having to throw an exception? I could not find this from the exception object.
Great article, you made me wonder how my custom exception to handle multiple validation errors would perform. And I had an interesting discovery. My custom exception ran faster than coldFusion's exceptions. Very bizarre.
Thanks or the blog entry.
I know this is a rather old post but I had a question about the method of the test. I'm just afraid that people may be taking this for more than it is. (Or I may be reading it incorrectly, feel free to correct me if I am.) It appears that you are comparing apples to oranges. You test the speed of doing an arrayAppend vs doing an arrayAppend and throwing an exception. It appears to me that this is always going to be much slower by virtue of the fact that you are doing more work, including creating an object which can be slow anyways.
I would think that to have a fair comparison you would need to do the same amount of work in a different way. For example, doing an arrayFind that with no exceptions you would return a 0 (instead of the array index) to denote that you did not find it and with exceptions, throwing an exception if it was not found. Then testing how long it took to run including testing for the 0 or catching the exception. I believe your results will probably still indicate that using the exceptions would be slower, but it would be more of an apples to apples comparison.
I don't advocate using exceptions for that type of situation, I'm just wondering if it would be more useful information.
I personally was looking for information about running script inside of a try/catch vs letting the application error handler deal with it. I modified your test to use the throwException parameter to wrap the arrayAppend with a try/catch when true and run it outside of the try/catch when false. I just wanted to see if the try/catch added overhead to the execution and from my limited testing it appears that it does not. It would be interesting to see if you find different results (I'm on cf8).
Keep up the good work on testing. It seems like most of the time I need information, you've already collected it.
Sorry for the second post. Just had another thought cross my mind. In my previous example, you may find a bigger disparity based on the number of misses you have as well. For example, in a test of 1000 runs, if you only have 1 miss, the exception case will only ever have 1 exception thrown vs the non-exception case having to test for 0 on every iteration. If you have 999 misses, you will have a lot more exceptions to deal with. It would be interesting to see at what point the exceptions are cheaper than the comparison. If I find some time I might have to dig into that.