On SpiderMonkey, a Javascript value is a 64-bit word. In most cases, 32 bits are set aside for the tag, and one possible tag is 'undefined value'. So checking to see if a value is undefined is basically a simple comparison, like checking if a pointer is NULL in C (except very slightly more work).
On the other hand, try/catch requires traversing some kind of structure to find the catch handler, and exceptions are often allocated somehow. This kind of action will need to inspect the stack, possibly look at the heap, and probably do many branches.
Plus, try/catch is not typically optimized for speed of catching exceptions. Exception handling is typically optimized so that the code path that doesn't throw an exception is almost as fast as it would be if the exception handling weren't there. You want people to use your exception handling so they can write more error-tolerant programs, but they might throw it out if it slows down their correct code. If you make sure you aren't slowing down the non-exception path, you may have to make tradeoffs that slow down the exception path.
The programming language shootout had a test of try/catch performance across languages. I remember Lisp was at the top, but not as if anyone cares -- try/catch performance isn't relevant to most programs.
OTOH, you may find it interesting that the reverse is true in Python. In Python, it is almost always faster to catch an exception rather than to check first. The way CPython checks for errors is by checking the return value of functions against NULL, after all, which is very fast. Checking ahead of time requires more Python code, and the Python code is going to be the slow part, at least on CPython.
I suspect the technical reason is because it hasn't been aggressively optimised. The reason for that is probably that it's not widely used in the real world, or at least not in performance-critical code. Especially in very tight code like that, I don't think there's a fundamental reason why the compiler couldn't detect the single throw site and compile the code down to the same as the conditional. It gets a bit messier when cascading an exception down the call stack.
One reason try/catch is slow is that it involves unwinding multiple stack frames, cleaning up as you go, until you hit a catch block.
Try/catch is also a very complicated flow control mechanism (think about how try/catch/finally/return all mesh together) such that a lot of VMs simply don't even attempt to apply any of their fancy optimizations to any function that contains a try/catch.
At least for Chrome, all optimisation is disabled in functions containing a try/catch. It's probably similar for the other browsers. Try/catch combined with some of the more sneaky semantics of javascript is rather a nightmare to optimise, I can imagine. Even a simple looking value can actually be a huge function chain (via defineProperty, defineGetter, or valueOf).
Comments
Why is try/catch so slow?
On SpiderMonkey, a Javascript value is a 64-bit word. In most cases, 32 bits are set aside for the tag, and one possible tag is 'undefined value'. So checking to see if a value is undefined is basically a simple comparison, like checking if a pointer is NULL in C (except very slightly more work).
On the other hand, try/catch requires traversing some kind of structure to find the catch handler, and exceptions are often allocated somehow. This kind of action will need to inspect the stack, possibly look at the heap, and probably do many branches.
Plus, try/catch is not typically optimized for speed of catching exceptions. Exception handling is typically optimized so that the code path that doesn't throw an exception is almost as fast as it would be if the exception handling weren't there. You want people to use your exception handling so they can write more error-tolerant programs, but they might throw it out if it slows down their correct code. If you make sure you aren't slowing down the non-exception path, you may have to make tradeoffs that slow down the exception path.
The programming language shootout had a test of try/catch performance across languages. I remember Lisp was at the top, but not as if anyone cares -- try/catch performance isn't relevant to most programs.
OTOH, you may find it interesting that the reverse is true in Python. In Python, it is almost always faster to catch an exception rather than to check first. The way CPython checks for errors is by checking the return value of functions against NULL, after all, which is very fast. Checking ahead of time requires more Python code, and the Python code is going to be the slow part, at least on CPython.
That's true - how about repeating the test with valid code that DOESN'T throw an exception.
- Edit -
Just tried it, it's not even close (on FF4):
tryCatch with undef - ~55,000
ifCheck with undef - ~2,000,000
tryCatch with Object - ~100,000,000
ifCheck with Object - ~2,000,000
So if an error is not thrown the try/catch expression is WAY faster.
I suspect the technical reason is because it hasn't been aggressively optimised. The reason for that is probably that it's not widely used in the real world, or at least not in performance-critical code. Especially in very tight code like that, I don't think there's a fundamental reason why the compiler couldn't detect the single throw site and compile the code down to the same as the conditional. It gets a bit messier when cascading an exception down the call stack.
One reason try/catch is slow is that it involves unwinding multiple stack frames, cleaning up as you go, until you hit a catch block.
Try/catch is also a very complicated flow control mechanism (think about how try/catch/finally/return all mesh together) such that a lot of VMs simply don't even attempt to apply any of their fancy optimizations to any function that contains a try/catch.
At least for Chrome, all optimisation is disabled in functions containing a try/catch. It's probably similar for the other browsers. Try/catch combined with some of the more sneaky semantics of javascript is rather a nightmare to optimise, I can imagine. Even a simple looking value can actually be a huge function chain (via defineProperty, defineGetter, or valueOf).