| nanoserv, bloat and performance » |
libevent for PHP
Libevent is a library that provides a mechanism to execute a callback function when a specific event occurs on a file descriptor or after a timeout has been reached.
Recently, an extension emerged in PECL (link) that provides bindings between libevent and PHP, and this if you ask me, is a very good thing.
Today, the only way to multiplex socket operations in PHP is by using a variation of the select() method. If you use the sockets extension, there is socket_select() and for the rest, stream_select(). These are good enough event interfaces for many applications, but they are not optimal when you must manage a large number of sockets. One of the reasons for this being the system select() call semantics, forcing you to regenerate huge bitfields every time you want to call it. Unfortunately, the PHP *_select() functions only wrap the select() syscall.
Follow up:
And here comes libevent.
So what are the advantages of using it in place of the standard PHP notification mechanisms ?
For a start, let's have a look at this performance comparison graph I ripped from the libevent homepage :
Here we see that for large number of open descriptors, select() can be as much as a thousand times slower that epoll() and kqueue(), both supported by libevent.
There is also another feature that make libevent even greater: input and output buffering. The PHP extension has two functions for this, event_buffer_read() and event_buffer_write(). They provide an interface to the libevent buffered events, here is an example from php.net :
<?php
function print_line($buf, $arg)
{
static $max_requests;
$max_requests++;
if ($max_requests == 10) {
event_base_loopexit($arg);
}
// print the line
echo event_buffer_read($buf, 4096);
}
function error_func($buf, $what, $arg)
{
// handle errors
}
$base = event_base_new();
$eb = event_buffer_new(STDIN, "print_line", NULL, "error_func", $base);
event_buffer_base_set($eb, $base);
event_buffer_enable($eb, EV_READ);
event_base_loop($base);
?>
In this example, an event buffer is created to hold data that is read from STDIN. When data comes in, it is read from the buffer using event_buffer_read() in the callback function, all this without leaving the main loop in event_base_loop(). This is much more than just a notification, because libevent does the actual data read and write.
To summarize, the libevent extension allow developers to build high performance asynchronous socket apps, by letting them implement only the core logic in PHP and takes care of every time-critical aspect using optimized C code and the best available event notification scheme provided by the host operating system.
When I find some time, I'd very much like to port the nanoserv backend to libevent, this should give a nice performance boost for high loads, as well as a possible reduction in memory usage, so stay tuned ...

