Exceptions

Optimizing exceptions

You might often hear about ex­cep­tions being slow. For this rea­son they are usu­ally shunned in the em­bed­ded space, and some­times even for reg­u­lar desk­top/server pro­gram­ming. What makes them slow? When one is thrown it needs to search through the call stack for ex­cep­tion han­dlers.

I guess I don’t un­der­stand this line of think­ing. For one, ex­cep­tions are meant for ex­cep­tional sit­u­a­tions: things you don’t ex­pect to hap­pen under nor­mal op­er­a­tion. Code that uses ex­cep­tions will run just as fast (or maybe even faster) as code with­out, until you throw one. These ex­cep­tional sit­u­a­tions are tru­ely rare, so I usu­ally don’t care if they do hap­pen to run slower.

A com­piler can ac­tu­ally use ex­cep­tions to op­ti­mize your code. Con­sider this in­ef­fi­cient (but typ­i­cal) pseudo-C:

int dosomething(void) {
   /* do something A */
   if(err) return -1;

   /* do something B */
   if(err) {
      /* cleanup previous work A */
      return -1;
   }

   /* do something C */
   if(err) {
      /* cleanup previous work B */
      /* cleanup previous work A */
      return -1;
   }

   return 0;
}

Or even this more ef­fi­cient (yes boys and girls, goto ac­tu­ally has a good use case in C, get over it) pseudo-C:

int dosomething(void) {
   /* do something A */
   if(err) return -1;

   /* do something B */
   if(err) goto err1;

   /* do something C */
   if(err) goto err2;

   return 0;

   err2:
   /* cleanup previous work B */

   err1:
   /* cleanup previous work A */

   return -1;
}

Why are these bad? Cache lo­cal­ity. In the first ex­am­ple, you have error han­dling code in­line with your reg­u­lar code. In the sec­ond you have it slightly bet­ter and off to the end of the func­tion. Ide­ally the code you run will all be com­pacted in as few cache lines as pos­si­ble, and er­ror­ing han­dling this way will waste sig­nif­i­cant space on cleanup code that in the large ma­jor­ity of cases won’t be run.

But with ex­cep­tions, the com­piler is free to take all the cleanup code in your en­tire app, and shove it into a sin­gle sep­a­rate area of code. All your nor­mal code that you ex­pect to run can be com­pact and closer to­gether. Of course, this will make ex­cep­tions run slower. If your code is heavy on throw­ing ex­cep­tions (which would prob­a­bly be an abuse) it will prob­a­bly cause a sig­nif­i­cant over­all slow­down. But if they are used cor­rectly–for ex­cep­tional sit­u­a­tions–then the com­mon case will be im­proved cache usage and there­for faster code.