On exceptions in D
Dmitry Olshansky
dmitry.olsh at gmail.com
Sun Feb 9 09:57:13 PST 2014
Split out of "List of Phobos functions that allocate memory?".
To reiterate, here is some critique, compiled:
1. Exceptions are class instances, hence (by default) are allocated on
GC heap. This is wrong default, GC is no place for temporaries.
2. Stack trace is constructed on throw. User pays no matter if the trace
is needed or not. This is in the works, thankfully.
3. Turns out message is expected to be a string, formatted apriori:
https://github.com/D-Programming-Language/druntime/blob/master/src/object_.d#L1306
Formatting a string in such setting inevitably allocates and it happens
at the throw site, even if nobody is using that message down the line.
At least one can override toString...
I thought I'd do something about it.
A seat of pants "solution" to avoid problems 1 and 3 (I do know it can
break under some unusual circumstances):
module fast_except;
class Failure(T...) : Exception
{
public:
this()
{
super("");
}
override void toString(scope void delegate(in char[]) sink) const
{
import std.format : formattedWrite;
sink(typeid(this).name);
sink(": ");
formattedWrite(sink, msg, args);
}
private:
void assign()(string msg, auto ref T args)
{
this.msg = msg;
this.args = args;
}
T args;
}
void risef(T...)(string fmt, T args)
{
static Failure!T slot;
if(!slot)
slot = new Failure!T();
slot.assign(fmt, args);
throw slot;
}
Now to testing. I used separate compilation and no optimization flags
whatsoever, and with the code below I get supposedly ~4 Millions of
try/catch per second on Linux x64. That is including extra overhead of a
function call and whatnot.
"Elapsed 2243 msec. Throughput 4.45766e+06/sec"
on
Intel(R) Core(TM) i5-4670 CPU @ 3.40GHz
//module #1
module fast_except;
//<<all of the above code here>>
void exceptional()
{
risef("All is lost! PI = %f", 3.17f);
}
//module #2
import std.datetime, std.stdio, fast_except, core.runtime;
void main()
{
//Runtime.traceHandler = null; //seems to change nothing
int count = 0;
StopWatch sw = StopWatch();
sw.start();
foreach(_; 0 .. 10_000_000)
{
try
{
exceptional();
}
catch(Exception e)
{
count++;
}
}
sw.stop();
writefln("Elapsed %s msec. Throughput %s/sec",
sw.peek().msecs, count*1e6/sw.peek().usecs);
}
--
Dmitry Olshansky
More information about the Digitalmars-d
mailing list