The cost of doing compile time introspection

Timon Gehr via Digitalmars-d digitalmars-d at puremagic.com
Thu May 11 14:09:05 PDT 2017


On 10.05.2017 16:03, Biotronic wrote:
>>
>
> A few things here - functions.fn would not do what you want, and neither
> would __traits(identifier).
> functions.fn would treat "fn" like a part of name, not a string value,
> so this will make the poor compiler barf.
> __traits(identifier, fn) expects fn to be a symbol, while here it's a
> string. In fact, it's exactly the string you want __traits to return.
> Lastly, you'll still need a mixin, whether it's for __traits(identifier,
> fn) or just fn - they're just strings. Something like this:
>
> static foreach (fn; CFunctions!functions) {
>     mixin("typeof(__traits(getMember, functions, fn))* "~fn~";");
> }

Yes, this works and is a few times faster.
It's slightly faster when inlining the condition:

static foreach(fn;__traits(allMembers, functions)){
     static if (isFunction!(__traits(getMember, functions, fn)) &&
                (functionLinkage!(__traits(getMember, functions, fn)) == 
"C" ||
                 functionLinkage!(__traits(getMember, functions, fn)) == 
"Windows")){
         mixin("typeof(functions."~fn~")* "~fn~";");
     }
}

With the DMD debug build, I measured the following times on my machine:

Baselines:

just imports:
0m0.318s

copy-pasted generated code after printing it with pragma(msg, ...):
0m0.341s

Compile-time code generation:

old version:
0m2.569s

static foreach, uninlined:
0m0.704s

static foreach inlined:
0m0.610s

Still not great, but a notable improvement.


isFunction and functionLinkage are slow, so I got rid of them (as well 
as the dependency on std.traits):

static foreach(fn;__traits(allMembers, functions)){
     static if(fn != "object" && fn != "llvm" && fn != "orEmpty"):
     mixin("typeof(functions."~fn~")* "~fn~";");
}

timing:
0m0.350s

(This is not perfect as you'll need to edit the list in case you are 
adding more non-c-function members to that module, but I guess it is a 
good trade-off.)



You can achieve essentially the same using a string mixin:

mixin({
     string r;
     foreach(fn;__traits(allMembers, functions))
         if(fn != "object" && fn != "llvm" && fn != "orEmpty")
             r~="typeof(functions."~fn~")* "~fn~";";
     return r;
}());

timing:
0m0.370s



In case the original semantics should be preserved, I think this is the 
best option:

mixin({
     string r;
     foreach(fn;CFunctions!functions)
         r~="typeof(functions."~fn~")* "~fn~";";
     return r;
}());

timing:
0m0.740s


More information about the Digitalmars-d mailing list