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

Reply via email to