"temporary" templates
Steven Schveighoffer
schveiguy at gmail.com
Tue Nov 26 17:03:04 UTC 2019
Every time you instantiate a template, it consumes memory in the
compiler for the duration of compilation.
For example, consider the silly template:
```
template SumAll(size_t x)
{
static if(x == 0) enum SumAll = 0;
enum SumAll = x + SumAll!(x - 1);
}
```
Instantiate this for SumAll!100_000, and you generate... oh wait. It
doesn't work (too much recursive templates). But THIS even sillier
template does:
```
template SumAll(size_t x)
{
static if(x == 0) enum SumAll = 0;
enum SumAll = SumAllHelper!(1, x);
}
template SumAllHelper(size_t min, size_t max)
{
static if(min == max)
{
enum SumAllHelper = min;
}
else
{
enum mid = (min + max) / 2;
enum SumAllHelper = SumAllHelper!(min, mid) + SumAllHelper!(mid
+ 1, max);
}
}
```
Compiling a program with SumAll!100000 consumes 1.4GB of RAM.
You'll notice that I'm pretty much NEVER going to instantiate
SumAllHelper directly, and never access ANY of the items inside the
template. Yet the compiler stores every single instantiation of
SumAllHelper "just in case" we need it again later. The cost for this
convenience is astronomical.
Even if I do SumAll!100001, it still adds another 300MB of templates,
saving *some* memory, but not enough to justify the caching.
If you look in std.meta, it's full of these style linear recursion or
divide-and-conquer recursion algorithms, often with a "Impl" pair to the
main template. Each one of these generates potentially hundreds or
thousands of template instances that are used ONCE.
But what if we could MARK SumAllHelper such that it's result should be
used once? That is, such that it's only generated temporarily, and then
thrown to the GC? This means that the memory consumption only happens
for SumAll, and not SumAllHelper (well, not permanently anyway). This
means that each call to SumAll is going to re-generate some templates of
SumAllHelper. But who cares? I'd rather have the thing compile with the
RAM I have then be uber-concerned with caching everything under the sun.
With the compiler now supporting garbage collection, this can be a
possibility. Is it feasible? Is it difficult? Do we actually need to
mark SumAllHelper with some @temporary attribute or can the compiler
figure it out?
Note, all these thoughts are happening because I am actually working on
reducing the memory footprint of a project that makes heavy use of
std.meta.NoDuplicates, which uses similar techniques as I've outlined
above and generates a LOT of templates. By switching to a different
design that avoids the need for it, I've reduced the compile-time
footprint from over 10GB to 3.5GB. But I wish it would "just work"
-Steve
More information about the Digitalmars-d
mailing list