TypeFunction example: ImplictConvTargets
Adam D. Ruppe
destructionator at gmail.com
Tue Oct 6 18:09:55 UTC 2020
On Tuesday, 6 October 2020 at 15:24:31 UTC, Bruce Carneal wrote:
> I believe we should aim for the simplest code that admits the
> desired performance.
Sure, but the desired performance here, for this stuff, is the
clear priority. Especially at lower levels where the costs
compound. Here's some princely advice: it is better to be both
feared and loved, but if you can be only one or the other, it is
better to be feared than to be loved. That's the reason why
Phobos does it the way it does. It'd love to have both, but if it
is one or the other, fast compiles are far more important than
pretty code.
> Well, what is a "real problem"?
That code compiles slowly and/or with excessive memory. That's
why type functions are being investigated.
There's no new functionality gained by the type functions. Their
whole reason for existing is to make faster, less memory hungry
builds. (right now anyway, that might change if it gains
capabilities, but right now an explicit design goal is to make
them a limited subset of template functionality in the name of
improved performance).
I frequently take Stefan's examples, copy/paste them into a
template, and use them. The code doesn't even look that different.
The problem is there's several usages where this version is slow.
type function version:
alias type = alias;
type[] basicTypeConvTargets(type T)
{
type[] targets;
targets.length = basic_types.length;
size_t n = 0;
foreach(t;basic_types)
{
if (is(T : t))
{
targets[n++] = t;
}
}
return targets[0 .. n];
}
template version:
alias type = string;
type[] basicTypeConvTargets(T)()
{
type[] targets;
targets.length = basic_types.length;
size_t n = 0;
foreach(t;basic_types)
{
if (is(T : mixin(t)))
{
targets[n++] = t;
}
}
return targets[0 .. n];
}
Very little difference! The makeConvMatix was *identical* except
for the function prototype (and even there, both return
strings!). Filter's guts can be:
size_t[Args.length] keep;
size_t pos = 0;
foreach(idx, alias arg; Args)
if(Pred!arg) keep[pos++] = idx; // note idx, not
arg.
return makeResult(keep[0 .. pos]);
In today's D. Again, *almost identical* to the typefunction
version, just using an index into the list instead of storing the
alias in the array directly.
What kills this approach is *not* the code being hideous. It is
the performance aspect - additional CTFE code is necessary to
convert it back to a type tuple, and secondarily, the foreach
being unrolled leads to extra work. A type function can simply do
`returned.tupleof` and keep the foreach how it is. Here, we'd
have to `mixin(returned.doConversion)`. (Where doConversion is a
similarly reusable lib function.)
Again, minor syntax difference, but significant performance hit
because doConversion must rebuild a new string out of stuff the
compiler already knows. Phobos' current implementation is uglier,
but also faster and uses less memory than the mixin version. So
it wins over it.
This is the place the typefunction has a potential win. It might
combine the nice code AND get a performance improvement. But if
the TF ends up slower than Phobos has now... it is going to be
rewritten into the faster version, even if uglier, because it is
the compile performance driving this evolution.
The Phobos implementation started life with a very simple
implementation too. It became what it is because it *had to*,
specifically for performance reasons.
More information about the Digitalmars-d
mailing list