Folding similar templates into one
Meta
jared771 at gmail.com
Wed Jun 26 19:03:34 PDT 2013
I apologize for the title, as I couldn't come up with something
succinct and descriptive enough while still being reasonably
short. We all know how powerful templates are in C++/D, but they
have that damning cost of needing to generate new code for each
new template argument. For example:
string format(string fmtStr)(T data)
{
...
}
As fmtStr is a compile-time template argument, the compiler will
have to generate N nearly-identical format functions for N
strings that format is instantiated with. This "template bloat"
increases exponentially as more template arguments are added.
Take std.exception.assertNotThrown:
void assertNotThrown(T : Throwable = Exception, E)
(lazy E expression,
string msg = null,
//Could be compile-time, but aren't,
//because of template bloat worries
string file = __FILE__,
size_t line = __LINE__)
{
try
expression();
catch(T t)
{
immutable message = msg.empty ? t.msg : msg;
immutable tail = message.empty ? "." : ": " ~ message;
throw new AssertError(format("assertNotThrown failed: %s
was thrown%s",
T.stringof,
tail),
file,
line,
t);
}
}
The arguments file and line are known at compile time, but should
not be made template arguments because there is a potential to
create N*M copies of assertNotThrown, given N values of __FILE__
and M values of __LINE__.
Adding large numbers of template arguments spuriously can cause
an explosion in binary size, especially when templates are very
heavily used, such as in most D code. Furthermore, programmers
worried about such bloat will undoubtedly take the safe route,
and try to move as many arguments as possible from the
compile-time parameter list to run-time. This means that
generated code will not necessarily be efficient as it should be.
I'd like to ask, however, why should this be? The arguments line
and file will only ever be strings and ints. They don't vary in
type, only value, and changing those values will not affect how
the function works. You could easily substitute out "exception.d"
with "stdio.d", and the functionality will be unchanged.
This remains true for values of the same type. It doesn't matter
whether you pass 1000, 10_000, or 100_000 as a template argument.
The way the function operates is not observably different (note
that for this to be true, we have to assume that static if does
not exist). This is in contrast to passing a completely different
type for T (say, RangeError), which may change how the function
works.
I'd like to know, are there any template "optimizations" that DMD
currently implements which mitigates such a situation such as
with assertNotThrown, where a compile-time argument may vary only
in value, and not type? Judging from the fact that this function
was changed so that that __FILE__ and __LINE__ are taking as
run-time parameters, I guess the answer is no. Is there anything
we can do about this?
More information about the Digitalmars-d
mailing list