Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The V8 performance issue with try/catch is widely misunderstood, and it causes people to unnecessarily avoid using it.

try/catch can slow down code in the same function as the try/catch statements. But it doesn't slow down any other function called from within there. In other words, it only affects the stack frame at which the try/catch statement actually appears.

So if you have some top level error trapping function like:

    errorCatcher = function(continuation){
      try {
        continuation();
      catch(err){
        reportErrorToServer(err);
      }
    }
and you only call it once at every entry point to your code, the impact will be totally undetectable, because there's only a single function invocation within the unoptimizable area.

I encourage people to performance test the difference caused by try/catch like this. You'll see that it doesn't hurt at all. It's only a problem if you actually write a try statement within some tight inner-loop function.



(Rewritten to report results)

I tried the following 4 variations in Node.js 0.6.13, V8 3.6.6.24. (The "global" business is for accessing the functions from a REPL.)

  var lots = 1000000;
  
  // A: try/catch inside loop
  global.a = function () {
      for (var i=0; i<lots; i++) {
          try { } catch (e) { }
      }
  }
  
  // B: one try/catch, inlined loop
  global.b = function () {
      try {
          for (var i=0; i<lots; i++) { }
      } catch (e) { }
  }
  
  // C: one try/catch, loop in local function
  global.c = function () {
      var loop = function () {
          for (var i=0; i<lots; i++) { }
      }
      try { loop(); } catch (e) { }
  }
  
  // D: one try/catch, loop in top-level function
  function loop () {
      for (var i=0; i<lots; i++) { }
  }
  global.d = function () {
      try { loop(); } catch (e) { }
  }
  
  // E: no try/catch
  global.e = function () { loop(); }
  
  global.time = function (times, fn) {
      var start = new Date().getTime();
      for (var i=0; i<times; i += 1) { fn(); }
      return (new Date().getTime() - start) / times;
  };
In the REPL:

  > ({ a : time(100,a), b : time(100,b), c : time(100,c), d : time(100,d), e : time(100,e) });
  { a 7.66 b 5.29 c 5.06 d 1.45 e 1.32 }
So a million try/catches (A) is bad. But hoisting the try outside the loop (B) or putting the loop into a local function (C) doesn't do nearly as well as putting the loop in an outside function (D), which is almost as fast as no try/catch at all (E). I tried a few variations (like making the loop actually do some work) and the outcomes were all comparable.

This clarifies and confirms what ef4 said: don't put cpu-intensive code inside the same function as a try/catch. Put it somewhere else and call it. Also, local functions don't necessarily count as "somewhere else".


My understanding is that each time the try is "executed" makes a performance hit.


(I've rewritten the GP)

That appears to be wrong, since B, C, and D all execute the same number of try/catches.


This is what I meant, though I guess I could've used a better term than "code paths". Thanks for clarifying this anyway. :)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: