AnyEvent
view release on metacpan or search on metacpan
I/O watcher waiting for input on one side of the socket. Each time the
socket watcher reads a byte it will write that byte to a random other
"server".
The effect is that there will be a lot of I/O watchers, only part of
which are active at any one point (so there is a constant number of
active fds for each loop iteration, but which fds these are is random).
The timeout is reset each time something is read because that reflects
how most timeouts work (and puts extra pressure on the event loops).
In this benchmark, we use 10000 socket pairs (20000 sockets), of which
100 (1%) are active. This mirrors the activity of large servers with
many connections, most of which are idle at any one point in time.
Source code for this benchmark is found as eg/bench2 in the AnyEvent
distribution. It uses the AE interface, which makes a real difference
for the EV and Perl backends only.
Explanation of the columns
*sockets* is the number of sockets, and twice the number of "servers"
(as each server has a read and write socket end).
*create* is the time it takes to create a socket pair (which is
nontrivial) and two watchers: an I/O watcher and a timeout watcher.
*request*, the most important value, is the time it takes to handle a
single "request", that is, reading the token from the pipe and
forwarding it to another server. This includes deleting the old timeout
and creating a new one that moves the timeout into the future.
Results
name sockets create request
EV 20000 62.66 7.99
Perl 20000 68.32 32.64
IOAsync 20000 174.06 101.15 epoll
IOAsync 20000 174.67 610.84 poll
Event 20000 202.69 242.91
Glib 20000 557.01 1689.52
POE 20000 341.54 12086.32 uses POE::Loop::Event
Discussion
This benchmark *does* measure scalability and overall performance of the
particular event loop.
EV is again fastest. Since it is using epoll on my system, the setup
time is relatively high, though.
Perl surprisingly comes second. It is much faster than the C-based event
loops Event and Glib.
IO::Async performs very well when using its epoll backend, and still
quite good compared to Glib when using its pure perl backend.
Event suffers from high setup time as well (look at its code and you
will understand why). Callback invocation also has a high overhead
compared to the "$_->() for .."-style loop that the Perl event loop
uses. Event uses select or poll in basically all documented
configurations.
Glib is hit hard by its quadratic behaviour w.r.t. many watchers. It
clearly fails to perform with many filehandles or in busy servers.
POE is still completely out of the picture, taking over 1000 times as
long as EV, and over 100 times as long as the Perl implementation, even
though it uses a C-based event loop in this case.
Summary
* The pure perl implementation performs extremely well.
* Avoid Glib or POE in large projects where performance matters.
BENCHMARKING SMALL SERVERS
While event loops should scale (and select-based ones do not...) even to
large servers, most programs we (or I :) actually write have only a few
I/O watchers.
In this benchmark, I use the same benchmark program as in the large
server case, but it uses only eight "servers", of which three are active
at any one time. This should reflect performance for a small server
relatively well.
The columns are identical to the previous table.
Results
name sockets create request
EV 16 20.00 6.54
Perl 16 25.75 12.62
Event 16 81.27 35.86
Glib 16 32.63 15.48
POE 16 261.87 276.28 uses POE::Loop::Event
Discussion
The benchmark tries to test the performance of a typical small server.
While knowing how various event loops perform is interesting, keep in
mind that their overhead in this case is usually not as important, due
to the small absolute number of watchers (that is, you need efficiency
and speed most when you have lots of watchers, not when you only have a
few of them).
EV is again fastest.
Perl again comes second. It is noticeably faster than the C-based event
loops Event and Glib, although the difference is too small to really
matter.
POE also performs much better in this case, but is is still far behind
the others.
Summary
* C-based event loops perform very well with small number of watchers,
as the management overhead dominates.
THE IO::Lambda BENCHMARK
Recently I was told about the benchmark in the IO::Lambda manpage, which
could be misinterpreted to make AnyEvent look bad. In fact, the
benchmark simply compares IO::Lambda with POE, and IO::Lambda looks
better (which shouldn't come as a surprise to anybody). As such, the
benchmark is fine, and mostly shows that the AnyEvent backend from
IO::Lambda isn't very optimal. But how would AnyEvent compare when used
without the extra baggage? To explore this, I wrote the equivalent
benchmark for AnyEvent.
( run in 1.733 second using v1.01-cache-2.11-cpan-39bf76dae61 )