Constructor performance in dmd

FreeSlave freeslave93 at gmail.com
Tue Dec 10 03:42:10 PST 2013


Hello, I wrote some performance test for different constructors 
of simple struct, and got some unexpected results.

Code:

//dmd vector.d -unittest
module vector;

debug(SVector)
{
     import std.stdio;
}

struct SVector(size_t dim, T = float)
{
private:
     T[dim] _arr;
public:
     alias dim dimension;
     alias dim size;

     this(this)
     {
         debug(SVector)
             writeln("copy constructor");
     }

     this(const T[dim] arr)
     {
         debug(SVector)
             writeln("constructor from static array");
         _arr = arr;
     }

     this(ref const T[dim] arr)
     {
         debug(SVector)
             writeln("constructor from ref static array");
         _arr = arr;
     }

     this(const(T)[] arr)
     {
         debug(SVector)
             writeln("constructor from dynamic array");
         assert(arr.length == dim);
         _arr = arr;
     }

     ref SVector opAssign(const SVector other)
     {
         debug(SVector)
             writeln("assign other vector");
         _arr = other._arr;
         return this;
     }

     ref SVector opAssign(ref const SVector other)
     {
         debug(SVector)
             writeln("assign other ref vector");
         _arr = other._arr;
         return this;
     }

     ref SVector opAssign(const T[dim] arr)
     {
         debug(SVector)
             writeln("assign static array");
         _arr = arr;
         return this;
     }

     ref SVector opAssign(ref const T[dim] arr)
     {
         debug(SVector)
             writeln("assign ref static array");
         _arr = arr;
         return this;
     }

     ref SVector opAssign(const(T)[] arr)
     {
         debug(SVector)
             writeln("assign dynamic array");
         assert(arr.length == dim);
         _arr = arr;
         return this;
     }
}


unittest
{
     import std.datetime;
     import std.stdio;
     void endWatch(ref StopWatch sw, string message)
     {
         sw.stop();
         writefln("%s: %s", message, sw.peek().msecs);
         sw.reset();
     }

     alias int scalar;
     alias SVector!(3, scalar) vec;

     writeln("SVector performance test");
     writefln("using %s as scalar type", scalar.stringof);

     StopWatch sw;

     enum n = 1_000_000;

     sw.start();
     foreach(i; 0..n)
     {
         vec temp = void;
     }
     endWatch(sw, "no init (void)");

     sw.start();
     foreach(i; 0..n)
     {
         vec temp;
     }
     endWatch(sw, "just init");

     sw.start();
     foreach(i; 0..n)
     {
         vec temp = vec.init;
     }
     endWatch(sw, "init from .init");

     sw.start();
     foreach(i; 0..n)
     {
         vec temp = vec();
     }
     endWatch(sw, "init from explicit constructor");

     sw.start();
     scalar[3] arr;
     foreach(i; 0..n)
     {
         vec temp = vec(arr);
     }
     endWatch(sw, "init from other vec");

     sw.start();
     vec v;
     foreach(i; 0..n)
     {
         vec temp = v;
     }
     endWatch(sw, "init from other ref vec");

     sw.start();
     foreach(i; 0..n)
     {
         vec temp = [1,2,3];
     }
     endWatch(sw, "init from static array");

     sw.start();
     foreach(i; 0..n)
     {
         vec temp = arr;
     }
     endWatch(sw, "init from ref static array");

     sw.start();
     scalar[] slice = arr[];
     foreach(i; 0..n)
     {
         vec temp = slice;
     }
     endWatch(sw, "init from slice");
}

int main()
{
     return 0;
}

Please, note 'scalar' alias, because I will change it later. Also 
note 'T[dim] _arr;' definition in SVector struct. Compile this 
example with dmd.

Tests show that we have really bad performance when we explicitly 
initialize our struct with vec.init and vec(). It's near 25 times 
slower than 'just init'.

Ok, let's change scalar from int to long.
Now initialization with vec.init is much faster, but still slower 
than 'just init'. It's weird, that 'long' version is faster to 
initialize. Same for float/double pair - 'double' version is 
initialized faster. Try it yourself! Initialization with vec() is 
still slow. But theoretically all these 3 ways should lead to 
same results.

Return to 'int' and do some magic. Add explicit initialization to 
static array defined in our struct:

T[dim] _arr = 0;

Now we have the best performance! Incredible. But it should be 
the same, right? Because, you know, default constructed array of 
ints have all zeros by default.

Let's leave this zero and change scalar to float. Again, very 
good performance. Now change from zero to T.init:

T[dim] _arr = T.init;

It becomes 5 times slower than zero version.

Also I should notice it looks like -O option does not help here.

ldc has pretty good results in all cases.


More information about the Digitalmars-d-learn mailing list