Profiling PHP coroutines

By Thomas di Luccio, on Apr 19, 2023

Asynchronous programming has become increasingly important for building high-performance applications. While the cornerstone of other languages, this is newly-charted territory for PHP. 

In this article, we’ll explore PHP coroutines, how they work, and how to use Blackfire in this context.

What are Coroutines?

A coroutine is a generalization of a subroutine, allowing a function to have multiple entry points for suspending and resuming execution. This powerful programming technique makes it easier to reason about complex logic flows. Coroutines are particularly useful for writing non-blocking, parallel, and concurrent code.

PHP and Coroutines

PHP 8.1 introduced native support of coroutines with Fibers. Fibers are a lightweight concurrency mechanism that allows developers to write asynchronous code using native PHP syntax, enabling more efficient and readable non-blocking code execution.

In parallel, several third-party libraries such as Swoole, Revolt PHP, and AmPHP have been developed to bring coroutine functionality to PHP. Those libraries aim to ease asynchronous PHP programming with convenient helper methods.

How do PHP Coroutines work?

PHP coroutines are built on top of Fibers. Fibers allow a function to yield control back to the caller, suspending its execution and preserving its internal state. When the generator is resumed, it continues executing from where it left off, retaining its previous state.

Will it Profile?

How does Blackfire handle asynchronous scripts? To discover this, let’s consider this simple PHP script based on Revolt:

use Revolt\EventLoop;

EventLoop::repeat(0.1, function ($callbackId): void {
	static $i = 0;

	if ($i++ < 5) {
    	echo "tick\n";

        	'Blackfire loves coroutines!',
        	['cost' => rand(8, 13)]

	} else {


print '++ Script end' . PHP_EOL;

We are resuming a function every 10ms to execute some code before being paused until the next tick. To simulate a more real-life situation, we are relying on password_hash, a PHP function that’s particularly resource-intensive.

Note: Although this code doesn’t use any Fiber explicitly, some are created internally by the internal Revolt EventLoop.

Let’s profile this script in debug mode to see every detail. The Debug addon allows for disabling the pruning of less impactful calls and anonymizing HTTP calls and SQL queries.

blackfire run --debug php event-loop.php

On the timeline view, we see the sequences of calls leading to our own code logic represented here by the password_hash function. Then, the execution is paused until the next tick (usleep). Behind the scenes, PHP keeps in memory the sequence of calls leading to that point for eventual later use.

When the execution of a Fiber is resumed, Blackfire re-displays in the timeline the historical sequence of calls so we understand better what led to the code being executed then. We can then clearly see the different coroutines being executed.

PHP coroutines for flexible and performant applications

PHP Fibers provide a powerful and flexible way to write high-performance, scalable, and maintainable code. By leveraging coroutines, developers can build modern web applications that efficiently handle I/O-bound tasks and complex logic flows. 

Please reach out! 

Meanwhile, we’re eager to hear from you about your experiences with PHP coroutines. How have you integrated Fibers into your projects? What challenges have you faced, and what successes have you achieved?

Find us on Twitter or Reddit, and share your stories, insights, and best practices. Your experiences can help inspire others and contribute to the ongoing evolution of PHP and the broader web development community. 

Happy Asynchronous Performance Optimization!

Thomas di Luccio

Thomas is a Developer Relations Engineer at for He likes nothing more than understanding the users' needs and helping them find practical and empowering solutions. He’ll support you as a day-to-day user.