vibe.d & exception performance

Marc Schütz" <schuetzm at gmx.net> Marc Schütz" <schuetzm at gmx.net>
Sun Feb 9 03:04:33 PST 2014


Dicebot recently posted about the use of exceptions as flow 
control in vibe.d, and how bad it performs:
http://forum.dlang.org/thread/ld0d79$2ife$1@digitalmars.com?page=8#post-lqchrhwplxeitefecwne:40forum.dlang.org

Now I did some tests (very unscientific) with a simple app, 
testing three things:

1) Normal program flow without exceptions

import std.random;
void index(HTTPServerRequest req, HTTPServerResponse res)
{
     auto i = uniform(0, int.max);
     res.render!("index.dt", req, i);
}

The template just outputs the generated random number, nothing 
fancy.

2) Throwing a new exception each time

void index(HTTPServerRequest req, HTTPServerResponse res)
{
     const exc = new HTTPStatusException(HTTPStatus.NotFound);
     throw exc;
}

3) Creating the exception once and reusing it

void index(HTTPServerRequest req, HTTPServerResponse res)
{
     static const exc = new 
HTTPStatusException(HTTPStatus.NotFound);
     throw exc;
}

Each of these variants I tested with two versions of vibe.d:

A) Unmodified vibe.d 0.7.17

B) vibe.d master with the following changes applied:

diff --git a/source/vibe/http/server.d b/source/vibe/http/server.d
index 2110cd2..7e29963 100644
--- a/source/vibe/http/server.d
+++ b/source/vibe/http/server.d
@@ -1395,10 +1395,11 @@ private bool handleRequest(Stream 
http_stream, TCPConnection tcp_connection, HTT
                 // if no one has written anything, return 404
                 enforceHTTP(res.headerWritten, 
HTTPStatus.notFound);
         } catch (HTTPStatusException err) {
-               logDebug("http error thrown: %s", 
err.toString().sanitize);
-               if (!res.headerWritten) errorOut(err.status, 
err.msg, err.toString(), err);
+
+//             logDebug("http error thrown: %s", 
err.toString().sanitize);
+               if (!res.headerWritten) errorOut(err.status, 
err.msg, /*err.toString()*/"", err);
                 else logDiagnostic("HTTPStatusException while 
writing the response: %s", err.msg);
-               logDebug("Exception while handling request %s %s: 
%s", req.method, req.requestURL, err.toString());
+//             logDebug("Exception while handling request %s %s: 
%s", req.method, req.requestURL, err.toString());
                 if (!parsed || res.headerWritten || 
justifiesConnectionClose(err.status))
                         keep_alive = false;
         } catch (Throwable e) {

This just disables calling `toString()` on the exception to avoid 
creating a backtrace, which according to Adam Ruppe's PR is slow:
https://github.com/D-Programming-Language/druntime/pull/717

Then I compiled with:
dub run --build=release
and used ab2 to benchmark:
ab2 -n 10000 -c 1000 -k -r http://localhost:8080/

The results:

                            A) vibe.d 0.7.17      B) vibe.d patched
1) no exceptions                13000 req/s            13000 req/s
2) dynamic exceptions             200 req/s              800 req/s
3) static exceptions              320 req/s            19000 req/s

(The smaller numbers are subject to a lot of variance. 
Additionally, for B/3 I increased the number of requests to 
100000 instead of 10000.)

Now, it can be clearly seen that not generating a backtrace 
results in 300% gain in throughput (A/2 vs. B/2). Also, reusing 
exceptions also gives us about 60% gain (A/2 vs. A/3). A/1 and 
B/1 are equal, which is to be expected.

But the big surprise is B/3. It seems as if there is no overhead 
at all! I thought this maybe comes from inlining, so I compiled 
with `--build=debug` which doesn't pass `-inline` to dmd, but it 
still served about 13000 req/s.

Does anyone have an idea what's going on here? Why the massive 
performance gain with B/3, when neither A/3 nor B/2 are so much 
faster? This indicates either a bug, or a really great 
opportunity for optimization.

In general: Is it even safe to reuse exceptions? Even assuming 
that caught exceptions arent't kept around, what would happen if 
the throwing function were called via different code paths? Would 
the stack trace be cached and kept around, and thus be 
potentially wrong? Does the language specification give any 
guarantees?


More information about the Digitalmars-d mailing list