Performance Impact of PHP Exceptions

Published On08 Sep 2020

Performance impact of PHP exceptions

Exceptions in programming languages provide a way for the application code to raise and handle exceptional situations, and PHP language is not an exception.

PHP provides various capabilities to use Exceptions in PHP code, such as global exception handlers, basic try/catch syntax, cascaded and combined catch syntax, catching exceptions just with its type, and the finally blocks.

In PHP 8.0, several of PHP internal functions will throw standard TypeError and ValueError exceptions, which means you will be seeing more and more exceptions and exception handling in PHP code.

This article tries to discover the performance impact of handling and throwing exceptions.

Test cases

Note that Exceptions with throw keyword, or the use of try/catch blocks has a very small execution time. It is highly unlikely that use of Exceptions is the bottleneck of any application.

This benchmark is meant to compare the relative performance impact of using Exceptions at all, and how it progressed over PHP versions.

In the benchmark, we will take a look at 6 test cases:

  1. No try/catch: Test without any exception handling at all.
  2. try/catch, return within try block: Return a value from inside a try block.
  3. try/catch, no exceptions: Use a try/catch block, but do not throw any Exception.
  4. try/multiple catch, no exceptions: Similar to #3, but with multiple cascading catch blocks.
  5. try/catch, throw exception: Catch the Exception from first catch block.
  6. try/multiple catch, throw exception: Catch the Exception from last catch block.

1. No try/catch

This is the base test that there are no any try/catch blocks at all.

function case_return(): ?int {
    return null;
}

2. try/catch, return within try

A simple try/catch block, but returns immediately within the try block. This is the first test case with a try/catch block, and should indicate the performance impact of using it, but not catching any exception, and should reflect the happy-path of an application.

function case_try_successful_return(): ?int {
    try {
        return null;
    }
    catch(Exception $ex) {}
}

3. try/catch, no exceptions

A try/catch block, but it does not return nor throw any exceptions. Another test case with try/catch blocks, but the code proceeds beyond it because no exceptions are thrown.

function case_try_successful_single_exception(): void {
    try {}
    catch(Exception $ex) {}
}

4. try/multiple catch, no exceptions

Similar to test case #3, but uses multiple cascading catch blocks.

function case_try_successful_multi_exception(): void {
    try {}
    catch(InvalidArgumentException $ex) {}
    catch(LogicException $ex) {}
    catch(Exception $ex) {}
}

5. try/catch, throw exception

This is the first test that throws an exception. This will indicate the performance impact of creating the Exception object in contrast to case #3.

function case_try_throw_single_exception(): void {
    try {
        throw new InvalidArgumentException();
    }
    catch(InvalidArgumentException $ex) {}
    catch(LogicException $ex) {}
    catch(Exception $ex) {}
}

6. try/multiple catch, throw exception

A try/catch block, but the exception is caught at the last catch clause. This test case is to see if there is any noticeable performance impact when PHP evaluates multiple catch blocks.

function case_try_throw_multi_exception(): void {
    try {
        throw new Exception();
    }
    catch(InvalidArgumentException $ex) {}
    catch(LogicException $ex) {}
    catch(Exception $ex) {}
}

Test subjects

The following PHP versions were tested:

  • 8.0 beta 3
  • 7.4.10
  • 7.3.22
  • 7.2.33

Test results

Comparison of execution times for PHP exceptions

7.2 7.3 7.4 8.0
1. No try/catch 0.0116 0.0085 0.0070 0.0060
2. try/catch, return within try{} 0.0159 0.0120 0.0100 0.0080
3. try/catch, no exceptions 0.0192 0.0161 0.0124 0.0085
4. try/multiple catch, no exceptions 0.0201 0.0150 0.0125 0.0081
5. try/catch, throw exception 0.1048 0.0732 0.0544 0.0540
6. try/catch, multiple catch blocks 0.1102 0.0680 0.0561 0.0550

Above are execution times average from 5 runs, with each case executed 100K times. Rounded to 4 decimals, and raw values are available here with up to 12 decimals.


Conclusion

Throughout all PHP versions tested, the try/catch blocks themselves have relatively small performance impact. However, throwing an Exception, which requires PHP to collect the stack trace and create an Exception object can be costly, and is up to 9 times slow.

Note that in absolute numbers, throwing an exception one million times took 0.5 seconds, which indicates that the absolute performance cost to throw an exception is quite low, and will not be a bottleneck in any application.

Exceptions are meant to be for... exceptional situations. Using them to replace a semantic function (such as a Book::exists($book_id)) might be semantically wrong, because a negative value (such as null) is still an expected result.

However, using Exceptions as a way to communicate exceptional situations that the code in context cannot handle, will be a perfect candidate to use an Exception.

In PHP 8.0, several internal functions start to throw Exceptions, including the new ValueError exception. So you use Exceptions where necessary, because although the absolute cost of throwing an exception is higher, it hardly makes a difference in the big scheme of things.

The benefits of properly using Exceptions, such as a global exception handler, the ability to further log and process said exceptions, and more cleaner architecture in unhappy-path handling far outweighs the performance cost of Exceptions.

Recent Articles on PHP.Watch

All ArticlesFeed 
How to fix `mysql_native_password` not loaded errors on MySQL 8.4

How to fix mysql_native_password not loaded errors on MySQL 8.4

How to fix the SQLSTATE[HY000] [1524] Plugin 'mysql_native_password' is not loaded errors caused in MySQL 8.4 no longer enabling the mysql_native_password plugin by default.
How to fix PHP Curl HTTPS Certificate Authority issues on Windows

How to fix PHP Curl HTTPS Certificate Authority issues on Windows

On Windows, HTTPS requests made with the Curl extension can fail because Curl has no root certificate list to validate the server certificates. This article discusses the secure and effective solutions, and highlights bad advice that can leave PHP applications insecure.
AEGIS Encryption with PHP Sodium Extension

AEGIS Encryption with PHP Sodium Extension

The Sodium extension in PHP 8.4 now supports AEGIS-128L and AEGIS256 Authenticated Encryption ciphers. They are significantly faster than AES-GCM and CHACHA20-POLY1305. This article benchmarks them and explains how to securely encrypt and decrypt data using AEGIS-128L and AEGIS256 on PHP.
Subscribe to PHP.Watch newsletter for monthly updates

You will receive an email on last Wednesday of every month and on major PHP releases with new articles related to PHP, upcoming changes, new features and what's changing in the language. No marketing emails, no selling of your contacts, no click-tracking, and one-click instant unsubscribe from any email you receive.

Support PHP.Watch — If you find the articles, version information, Codex, and other PHP.Watch contributions useful, consider supporting through GitHub Sponsors. Your sponsorship helps dedicate more time to creating valuable content and improving the PHP community. Together, we can keep the momentum going — thank you for your support!

Thanks to the highest tier sponsor: @TomasVotruba for your generous support to keep PHP.Watch moving 💜