The cost of doing compile time introspection
Moritz Maxeiner via Digitalmars-d
digitalmars-d at puremagic.com
Wed May 10 04:45:05 PDT 2017
So, after being asked to support dynamic loading in llvm-d[1] at
DConf 2017 I essentially had the following two options:
- have two separate declarations of the bindings, one with the
function signatures (linking) and one with function pointers
(loading)
- Have one declaration and derive the other at compile time
Since this is D and I was reminded of Andrei's keynote I thought
to myself "use the Compiler, Dummy" and went about
introspecting[2] the function signatures[3]. The relevant code
looks like this:
---
import functions = llvm.functions.link;
template isCFunction(alias scope_, string member)
{
static if (isFunction!(__traits(getMember, scope_, member)) &&
(functionLinkage!(__traits(getMember, scope_,
member)) == "C" ||
functionLinkage!(__traits(getMember, scope_,
member)) == "Windows")) {
enum isCFunction = true;
} else {
enum isCFunction = false;
}
}
template CFunctions(alias mod)
{
alias isCFunction(string member) = .isCFunction!(mod, member);
alias CFunctions = Filter!(isCFunction, __traits(allMembers,
mod));
}
string declareStubs()
{
import std.array : appender;
auto code = appender!string;
foreach (fn; CFunctions!functions) {
code.put("typeof(functions."); code.put(fn);
code.put(")* "); code.put(fn); code.put(";\n");
}
return code.data;
}
mixin (declareStubs);
---
Now, the above code essentially searches through all symbols in
the llvm.functions.link module, filters out anything that's not
an extern(System) function and then generates code declaring a
function pointer for each of them, and then finally mixes that
code in.
This increases compilation time on my Broadwell i5-5200U from
faster than my terminal emulator can print to 4 seconds. Yikes.
From experience I knew that it wouldn't be cheap, but that's a
hefty cost for a conceptually simple use case (from my PoV).
You might ask why I need to use CTFE here (as the interpreter is
not cheap): It's because I want the mixed-in function pointers to
be at module scope and I'm not aware of any other way to do that
currently; if the new static foreach works the way I suspect,
then that part could essentially be replaced (at module scope)
with something like
---
static foreach (fn; CFunctions!functions) {
typeof(functions.fn)* __traits(identifier, fn);
}
---
Looks much nicer, but will it be fast? I don't know, but I hope
so, since it shouldn't need to spin up CTFE.
TLDR: Compile time introspection still increases compile time
from below human response time (i.e. without encouraging context
switching in your brain) to breaking your concentration even for
simple use cases.
[1] https://github.com/Calrama/llvm-d
[2]
https://github.com/Calrama/llvm-d/blob/master/source/llvm/functions/load.d
[3]
https://github.com/Calrama/llvm-d/blob/master/source/llvm/functions/link.d
More information about the Digitalmars-d
mailing list