libevent vs libuv
Comparing libevent and libuv. My upfront biased: I want to like libevent. However, I want to objectively compare the two and make an informed decision.
Both libraries were easy to build.
libevent failed to build correctly the first time. Nothing major, it just couldn't find an OpenSSL header. It was otherwise straightforward to build.
$ ./configure --prefix=$PWD/dist --disable-openssl
$ make && make install
Building libuv was straightforward. Since it was cloned from GitHub I had to run autogen.sh.
$ sh autogen.sh
$ ./configure --prefix=$PWD/dist
$ make && make install
The most superficial comparison we can make is a size comparison of the compiled static lib. While superficial, library size does matter for certain applications.
NOTE: this isn't the whole story. libevent_core.a
is smaller than
libuv.a
and may be all that is needed. libevent.a
is deprecated
and one is supposed to link with libevent_core.a
and the link
against the optional libraries as needed.
Both code bases use a K&Rish style. libevent uses tab for indention while libuv uses 2-spaces. With my editor's tabs defaulting to a width of 8 it makes libevent look more complex due to deep levels of indention, but this is superficial... Setting the width to 2 make the indention levels look about equal. libuv probably has an advantage here as overall the functions are shorter and the levels of depth less.
In libevent not every public function is prefixed with ev
some stuff
uses event_
while others use bufferevent_
, etc. In libuv it seems
every public function is prefixed with uv_
.
libevent's headers have headerdoc style comments while libuv does not. libuv documentation is separated from the source.
To my surprise libuv scored slightly worse than libevent. The difference is negligible and neither library was riddle with unmaintainable code.
$ complexity --histogram --score --thresh=7 ~/src/libevent-2.0.22-stable/*.c
libevent complexity results:
Complexity Histogram
Score-Range Lin-Ct
0-9 1936 ************************************************************
10-19 1067 *********************************
20-29 175 *****
30-39 136 ****
40-49 103 ***
Scored procedure ct: 49
Non-comment line ct: 3417
Average line score: 12
25%-ile score: 8 (75% in higher score procs)
50%-ile score: 9 (half in higher score procs)
75%-ile score: 13 (25% in higher score procs)
Highest score: 44 (epoll_apply_one_change() in epoll.c)
Unscored procedures: 1
libevent's most complex function is for some reason pointlessly
wrapped in an if block that is always true. This inflates its
complexity score. The if block's conditional is if (1)
and it wraps
everything but the variable declarations and the final return
statement.
The logic of the most complex function starts with an ominous warning
comment /* The logic here is a little tricky. [sic] */
. It is
strikingly aware of the needless complexity of the
function. Continuing on to another comment /* TODO: Turn this into a switch or a table lookup. */
. At least the libevent contributors know
where their problem code is and have a plan for it.
Even with the added indention depth the deepest code is 5 levels. It
would only be 4 with the if (1)
conditional removed. The function
comes in a 154 lines.
$ complexity --histogram --score --thresh=7 ~/src/libuv/src/*.c ~/src/libuv/src/unix/*.c
libuv complexity results:
Complexity Histogram
Score-Range Lin-Ct
0-9 804 ****************************************
10-19 1212 ************************************************************
20-29 573 ****************************
30-39 0
40-49 173 *********
Scored procedure ct: 39
Non-comment line ct: 2762
Average line score: 15
25%-ile score: 9 (75% in higher score procs)
50%-ile score: 12 (half in higher score procs)
75%-ile score: 20 (25% in higher score procs)
Highest score: 49 (uv__io_poll() in src/unix/kqueue.c)
Unscored procedures: 1
libuv's most complex functions starts by declaring 18 local variables and the line count comes in at 227 lines.
The function is deeply nested loops and if conditionals. The deepest level of indention is 7 levels.
While it is tempting to declare this terrible code, I will withhold judgment because I'm unfamiliar with these libraries and their implementations. I truly can't say I can do better, yet...
I used to prefer 2-space indentation, but I'm starting to question that preference. I'm now starting to think it actually makes code more difficult to read and worse it makes bad code look deceptively simple.
To gauge the easy of use I rewrote two sample programs, one for each library. The simplest useful program that can be created with each library is an HTTP echo server. Links to the respective GitHub repos are below:
forthcoming
Not only memory consumption, but how easy it is to estimate and reason about the memory characteristics of programs written with each respective library.
forthcoming
forthcoming