We had a little bit of a discussion on twitter about the performance implications of errors in php.
I won’t surprise you as it is (should be) well-known that the error generation and the error suppression is slow. See this blogpost from Derick Rethans: http://derickrethans.nl/five-reasons-why-the-shutop-operator-should-be-avoided.html
So basically @$foo = bar; is slow because:
- an error will be generated and formatted regardless the error_reporting level or the existence of a custom error handler, because you can access the last error through error_get_last and $php_errormsg
- the @ operator only wraps the statement between two ini call: setting the error_reporting to 0, and restoring it to the original value. this is 2 more call to be executed and as you would guess, this means if you have a custom error handler, where you didn’t set the second $error_types parameter in your set_error_handler call, your custom error handler will be executed on the suppressed errors.
- as Derick pointed out, the Zend Engine will generate slower code to the suppressed statements.
I’ve just wrote a little benchmark on the issue, available on github
Basically it executes an assigment in a for loop, either using the string ‘foo’ or the non-existent constant foo, which will fallback to ‘foo’ and trigger a notice (I know, it is not a perfect test, because the constant lookup also has some overhead, but still better and more close to the real life issues than trigger_error()).
I mixed this with using the suppress operator and a custom error handler, here are the results:
tyrael@thor:~/checkouts/php-microbenchmarks$ ./src/testError/test.sh
Executing baseline.php
real 0m1.280s
user 0m1.257s
sys 0m0.023s
Executing baseline+error.php
real 0m7.299s
user 0m7.276s
sys 0m0.022s
Executing baseline+error_handler.php
real 0m1.284s
user 0m1.255s
sys 0m0.029s
Executing baseline+suppression.php
real 0m2.035s
user 0m2.011s
sys 0m0.021s
Executing baseline+error+error_handler.php
real 0m20.405s
user 0m20.377s
sys 0m0.021s
Executing baseline+error_handler+suppression.php
real 0m2.000s
user 0m1.973s
sys 0m0.027s
Executing baseline+error+suppression.php
real 0m8.293s
user 0m8.271s
sys 0m0.021s
Executing baseline+error+error_handler+suppression.php
real 0m20.869s
user 0m20.842s
sys 0m0.020s
- As you can see the overhead of having a custom error handler is almost negligible if it isn’t called.
- The suppression operator adds a small overhead without errors. (~1.6X)
- Having an error without @ or custom error handler still slow (~5.7X).
- if you have an error and a custom error handler which gets executed, that yields for a ~10X performance loss, regardless of using the suppression operator or not.
So my advices:
- if you see errors in your log, fix them, thats also a performance gain.
- if you use custom error handler, don’t forget to set the $error_types, you can use bitmask like for error_reporting(in our case E_ALL &~ E_NOTICE), or you can pass the error_reporting() to set your handler to the same level as your error_reporting.
- don’t overuse the @ operator, and if you do, always handle the return values of your suppressed statements, or you will suck big time when you have to debug your application (check out the scream pecl extension or xdebug.scream)