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