www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - New blog post on the cost of compile time

reply Steven Schveighoffer <schveiguy gmail.com> writes:
In this post: https://forum.dlang.org/post/tm3p0p$2js2$1 digitalmars.com

I mentioned:

 I did a test on something I was working on for my talk, and I'm going to write
a blog post about it, because I'm kind of stunned at the results.
Well, I finally got around to it: https://www.schveiguy.com/blog/2023/01/the-cost-of-compile-time-in-d/ Let me know what you think. -Steve
Jan 15 2023
next sibling parent reply Arjan <arjan ask.me.to> writes:
On Monday, 16 January 2023 at 04:30:25 UTC, Steven Schveighoffer 
wrote:
 https://www.schveiguy.com/blog/2023/01/the-cost-of-compile-time-in-d/

 Let me know what you think.
I think it is marveleous. Wondering are there any down sides to using typeof?
Jan 16 2023
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/16/23 3:10 AM, Arjan wrote:
 On Monday, 16 January 2023 at 04:30:25 UTC, Steven Schveighoffer wrote:
 https://www.schveiguy.com/blog/2023/01/the-cost-of-compile-time-in-d/

 Let me know what you think.
I think it is marveleous. Wondering are there any down sides to using typeof?
In this instance, no. This was just a case of "oh, look, there's ReturnType, so I can just use that" instead of trying to actively avoid using the tools in std.traits. But in general, we still want to be able to use the cool tools that Phobos gives us without too much penalty, so the larger problem still remains -- templates just should be better performing. In general, Phobos templates should try to avoid using simple wrappers for internal things. One thing I didn't discuss in the post is that the `ReturnType` instances here are only ever going to be instantiated *once*, and on something that is *never used* (the lambda function). Once the boolean for `isInputRange` is decided, there is no reason to keep that stuff around. Some way to cull those templates from the cache would be most welcome. -Steve
Jan 16 2023
parent reply Nick Treleaven <nick geany.org> writes:
On Monday, 16 January 2023 at 15:13:04 UTC, Steven Schveighoffer 
wrote:
 In general, Phobos templates should try to avoid using simple 
 wrappers for internal things. One thing I didn't discuss in the 
 post is that the `ReturnType` instances here are only ever 
 going to be instantiated *once*, and on something that is 
 *never used* (the lambda function).
If each test used `lvalueOf` from std.traits then the instantiations would be reused (there would be 2 just in isInputRange), and likely elsewhere too. There could be a convention to use lvalueOf too and avoid instantiating rvalueOf for instantiation reuse. ```d is(typeof(() { return (*cast(R*)null).empty; }()) == bool) is(typeof(() { return lvalueOf!R.empty; }()) == bool) ``` That looks a bit nicer as it says what its intent is. Hopefully it wouldn't affect memory/performance much. Another idea is to factor out all the `r` parameter declarations into one, and use that for the return type tests too: ```d // now enum isInputRange(R) = is(typeof(R.init) == R) && is(typeof(() { return (*cast(R*)null).empty; }()) == bool) && (is(typeof((return ref R r) => r.front)) || is(typeof(ref (return ref R r) => r.front))) && !is(typeof(() { return (*cast(R*)null).front; }()) == void) && is(typeof((R r) => r.popFront)); // factored enum isInputRange(R) = __traits(compiles, (R r) { static assert( is(typeof(R.init) == R) && is(typeof({ return r.empty; }()) == bool) && is(typeof(() return => r.front)) && is(typeof(ref () return => r.front)) && !is(typeof({ return r.front; }()) == void) && is(typeof({ r.popFront; })) ); }); ``` The factored version looks much easier to read for me. I don't know how they compare for memory/performance though.
Feb 18 2023
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/18/23 7:04 AM, Nick Treleaven wrote:
 On Monday, 16 January 2023 at 15:13:04 UTC, Steven Schveighoffer wrote:
 In general, Phobos templates should try to avoid using simple wrappers 
 for internal things. One thing I didn't discuss in the post is that 
 the `ReturnType` instances here are only ever going to be instantiated 
 *once*, and on something that is *never used* (the lambda function).
If each test used `lvalueOf` from std.traits then the instantiations would be reused (there would be 2 just in isInputRange), and likely elsewhere too. There could be a convention to use lvalueOf too and avoid instantiating rvalueOf for instantiation reuse. ```d is(typeof(() { return (*cast(R*)null).empty; }()) == bool) is(typeof(() { return lvalueOf!R.empty; }()) == bool) ``` That looks a bit nicer as it says what its intent is. Hopefully it wouldn't affect memory/performance much. Another idea is to factor out all the `r` parameter declarations into one, and use that for the return type tests too: ```d // now enum isInputRange(R) =     is(typeof(R.init) == R)     && is(typeof(() { return (*cast(R*)null).empty; }()) == bool)     && (is(typeof((return ref R r) => r.front)) ||         is(typeof(ref (return ref R r) => r.front)))     && !is(typeof(() { return (*cast(R*)null).front; }()) == void)     && is(typeof((R r) => r.popFront)); // factored enum isInputRange(R) =     __traits(compiles, (R r) {         static assert(             is(typeof(R.init) == R) &&             is(typeof({ return r.empty; }()) == bool) &&             is(typeof(() return => r.front)) &&             is(typeof(ref () return => r.front)) &&             !is(typeof({ return r.front; }()) == void) &&             is(typeof({ r.popFront; }))         );     }); ``` The factored version looks much easier to read for me. I don't know how they compare for memory/performance though.
This is how isInputRange used to look. The reason it was changed is because the compiler now "sees" the different clauses separated by &&, and will tell you which one failed. When you wrap it like this, it just sees one big constraint. I actually did get a PR merged. It wasn't as simple as I had written in that blog post, due to the stupid `inout` requirement that `inout` data can only be used inside a function with an `inout` parameter. I did start with using `lvalueOf!R`, but then realized that since `isInputRange` validates that `typeof(R.init) == R`, I just used `R.init` as the parameter. See the merged PR here: https://github.com/dlang/phobos/pull/8682 -Steve
Feb 18 2023
parent reply Nick Treleaven <nick geany.org> writes:
On Saturday, 18 February 2023 at 17:16:18 UTC, Steven 
Schveighoffer wrote:
 This is how isInputRange used to look. The reason it was 
 changed is because the compiler now "sees" the different 
 clauses separated by &&, and will tell you which one failed. 
 When you wrap it like this, it just sees one big constraint.
Makes sense, although I don't get that on my machine with dmd v2.101.0: ```d import std.range; int f(R)() if (isInputRange!R) => 2; int i = f!int; ``` ``` isinputrange.d(68): Error: template instance `isinputrange.f!int` does not match template declaration `f(R)()` with `R = int` must satisfy the following constraint: ` isInputRange!R` ``` That's it, nothing about why isInputRange failed. Maybe I'm doing something wrong. Anyway, assuming dmd can use that I wonder if this would work (aside from the inout issue): ```d template isInputRange(R) { extern R r; // dummy enum isInputRange = is(typeof(R.init) == R) && is(typeof({ return r.empty; }()) == bool) && (is(typeof(() return => r.front)) || is(typeof(ref () return => r.front))) && !is(typeof({ return r.front; }()) == void) && is(typeof({ r.popFront; })); } ``` dmd could still see through the eponymous template to the expression, in theory.
 I actually did get a PR merged. It wasn't as simple as I had 
 written in that blog post, due to the stupid `inout` 
 requirement that `inout` data can only be used inside a 
 function with an `inout` parameter.
OK, so `typeof((R r) { return r.empty; } (R.init))` works with inout. Thanks for the blog post BTW.
 I did start with using `lvalueOf!R`, but then realized that 
 since `isInputRange` validates that `typeof(R.init) == R`, I 
 just used `R.init` as the parameter.
Is that validation to detect types with redefined `init`? I didn't realize Phobos needs to care about that.
Feb 18 2023
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Saturday, 18 February 2023 at 19:25:01 UTC, Nick Treleaven 
wrote:
 I did start with using `lvalueOf!R`, but then realized that 
 since `isInputRange` validates that `typeof(R.init) == R`, I 
 just used `R.init` as the parameter.
Is that validation to detect types with redefined `init`? I didn't realize Phobos needs to care about that.
There is at least one project in the project tester that uses redefined `init`, so yes, Phobos has to care about it (at least until the deprecation [1] goes through). [1] https://github.com/dlang/dmd/pull/12512
Feb 18 2023
parent ryuukk_ <ryuukk.dev gmail.com> writes:
On Saturday, 18 February 2023 at 20:04:27 UTC, Paul Backus wrote:
 On Saturday, 18 February 2023 at 19:25:01 UTC, Nick Treleaven 
 wrote:
 I did start with using `lvalueOf!R`, but then realized that 
 since `isInputRange` validates that `typeof(R.init) == R`, I 
 just used `R.init` as the parameter.
Is that validation to detect types with redefined `init`? I didn't realize Phobos needs to care about that.
There is at least one project in the project tester that uses redefined `init`, so yes, Phobos has to care about it (at least until the deprecation [1] goes through). [1] https://github.com/dlang/dmd/pull/12512
 Declaring tupleof, stringof, min, max and maybe others as 
 members should also be deprecated in the future.
min, max? why??? i hope that won't happen anytime soon init/deinit can't be used create/destroy can't be used Maybe it's time to introduce something, a token for builtin features?
Feb 18 2023
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/18/23 2:25 PM, Nick Treleaven wrote:
 On Saturday, 18 February 2023 at 17:16:18 UTC, Steven Schveighoffer wrote:
 This is how isInputRange used to look. The reason it was changed is 
 because the compiler now "sees" the different clauses separated by &&, 
 and will tell you which one failed. When you wrap it like this, it 
 just sees one big constraint.
Makes sense, although I don't get that on my machine with dmd v2.101.0: ```d import std.range; int f(R)() if (isInputRange!R) => 2; int i = f!int; ``` ``` isinputrange.d(68): Error: template instance `isinputrange.f!int` does not match template declaration `f(R)()`   with `R = int`   must satisfy the following constraint: `       isInputRange!R` ``` That's it, nothing about why isInputRange failed. Maybe I'm doing something wrong.
uhh... I think I am wrong on the reasoning then. I thought it would keep going down into the constraint, but it doesn't? Maybe it was planned? I do know that && in the constraint itself does do that. e.g.: ```d void foo(R)(R r) if (isInputRange!R && !is(R == int[])) {} void main() { foo(1); foo([1]); } ``` ``` onlineapp.d(8): Error: none of the overloads of template `onlineapp.foo` are callable using argument types `!()(int)` onlineapp.d(2): Candidate is: `foo(R)(R r)` with `R = int` must satisfy the following constraint: ` isInputRange!R` onlineapp.d(9): Error: none of the overloads of template `onlineapp.foo` are callable using argument types `!()(int[])` onlineapp.d(2): Candidate is: `foo(R)(R r)` with `R = int[]` must satisfy the following constraint: ` !is(R == int[])` ``` and I also do know that the original `isInputRange` code was like you proposed -- one lambda with all the things.
 Anyway, assuming dmd can use that I wonder if this would work (aside 
 from the inout issue):
 ```d
 template isInputRange(R) {
      extern R r; // dummy
      enum isInputRange =
          is(typeof(R.init) == R) &&
          is(typeof({ return r.empty; }()) == bool) &&
          (is(typeof(() return => r.front)) ||
              is(typeof(ref () return => r.front))) &&
          !is(typeof({ return r.front; }()) == void) &&
          is(typeof({ r.popFront; }));
 }
 ```
 dmd could still see through the eponymous template to the expression, in 
 theory.
I don't know if that works, would it fail to link? It's an interesting idea.
 
 I actually did get a PR merged. It wasn't as simple as I had written 
 in that blog post, due to the stupid `inout` requirement that `inout` 
 data can only be used inside a function with an `inout` parameter.
OK, so `typeof((R r) { return r.empty; } (R.init))` works with inout.
Yes, that's what I found that worked. You need the lambda parameter to mimic the mutability of the type. Which kind of sucks, but it's what we have right now. `lvalueOf!R` would work too, I just figured it isn't needed. I wonder what the cost of using `T.init` is vs. a cached lookup of a template. It's probably pretty small, but these are the kinds of things that are unintuitive.
 
 Thanks for the blog post BTW.
You're welcome! I write blog posts too infrequently, but I do have a bunch of half-started ones. I'm working on another right now...
 I did start with using `lvalueOf!R`, but then realized that since 
 `isInputRange` validates that `typeof(R.init) == R`, I just used 
 `R.init` as the parameter.
Is that validation to detect types with redefined `init`? I didn't realize Phobos needs to care about that.
I believe so. I'm not 100% sure why we are checking for redefined init here, but apparently it's needed somewhere inside phobos. -Steve
Feb 19 2023
parent Nick Treleaven <nick geany.org> writes:
On Sunday, 19 February 2023 at 18:08:28 UTC, Steven Schveighoffer 
wrote:
 On 2/18/23 2:25 PM, Nick Treleaven wrote:
 That's it, nothing about why isInputRange failed. Maybe I'm 
 doing something wrong.
uhh... I think I am wrong on the reasoning then. I thought it would keep going down into the constraint, but it doesn't? Maybe it was planned?
OK, it could be implemented. Perhaps with complex failing constraints it would be too much information if it recursed into each template predicate too (though could be done with `-v`).
 (aside from the inout issue):
 ```d
 template isInputRange(R) {
      extern R r; // dummy
      enum isInputRange =
          is(typeof(R.init) == R) &&
          is(typeof({ return r.empty; }()) == bool) &&
          (is(typeof(() return => r.front)) ||
              is(typeof(ref () return => r.front))) &&
          !is(typeof({ return r.front; }()) == void) &&
          is(typeof({ r.popFront; }));
 }
 ```
 dmd could still see through the eponymous template to the 
 expression, in theory.
I don't know if that works, would it fail to link? It's an interesting idea.
It doesn't cause a link error on my Linux machine, probably the linker detects it's not actually used anywhere. But declaring an `extern` variable of a type that contains `inout` data is an error: ``` isinputrange.d(54): Error: variable `isinputrange.isInputRange!(inout(int)[]).r` only parameters or stack based variables can be `inout` ``` So this pattern won't work.
 I actually did get a PR merged. It wasn't as simple as I had 
 written in that blog post, due to the stupid `inout` 
 requirement that `inout` data can only be used inside a 
 function with an `inout` parameter.
OK, so `typeof((R r) { return r.empty; } (R.init))` works with inout.
Yes, that's what I found that worked. You need the lambda parameter to mimic the mutability of the type. Which kind of sucks, but it's what we have right now.
Would be nice if that rule didn't apply when the `inout` data is a member of another type you're instantiating.
 Thanks for the blog post BTW.
You're welcome! I write blog posts too infrequently, but I do have a bunch of half-started ones. I'm working on another right now...
Great!
Feb 19 2023
prev sibling next sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Monday, 16 January 2023 at 04:30:25 UTC, Steven Schveighoffer 
wrote:
 In this post: 
 https://forum.dlang.org/post/tm3p0p$2js2$1 digitalmars.com

 I mentioned:

 I did a test on something I was working on for my talk, and 
 I'm going to write a blog post about it, because I'm kind of 
 stunned at the results.
Well, I finally got around to it: https://www.schveiguy.com/blog/2023/01/the-cost-of-compile-time-in-d/ Let me know what you think. -Steve
Looks like :handwaves: given a 2.4Ghz processor, that'd be 150k cycles per ReturnType instantiation? Not super much, but not nothing either. If that distributes over five templates, it'd be something like 30k for a template instantiation in general. For something that hits the allocator a few times, that seems ... about right? Indicating to me that if we want this to be fast, we have to find a way to make a template instantiation *do less.* I think that's gonna be a hard sell, given that the instantiation absolutely has to make a copy of the entire template body AST given the compiler design as it is. Looking at a profiler, how would you say these cycles are distributed between "copy tree" and "walk tree for semantic"?
Jan 16 2023
next sibling parent max haughton <maxhaton gmail.com> writes:
On Monday, 16 January 2023 at 09:12:59 UTC, FeepingCreature wrote:
 On Monday, 16 January 2023 at 04:30:25 UTC, Steven 
 Schveighoffer wrote:
 In this post: 
 https://forum.dlang.org/post/tm3p0p$2js2$1 digitalmars.com

 I mentioned:

 I did a test on something I was working on for my talk, and 
 I'm going to write a blog post about it, because I'm kind of 
 stunned at the results.
Well, I finally got around to it: https://www.schveiguy.com/blog/2023/01/the-cost-of-compile-time-in-d/ Let me know what you think. -Steve
Looks like :handwaves: given a 2.4Ghz processor, that'd be 150k cycles per ReturnType instantiation? Not super much, but not nothing either. If that distributes over five templates, it'd be something like 30k for a template instantiation in general. For something that hits the allocator a few times, that seems ... about right?
Processors these days are both faster than that and have pretty good IPC compiling D code so it's worse.
 Indicating to me that if we want this to be fast, we have to 
 find a way to make a template instantiation *do less.* I think 
 that's gonna be a hard sell, given that the instantiation 
 absolutely has to make a copy of the entire template body AST 
 given the compiler design as it is.
A lot of these copies are made defensively — some of them are actually required, or at least require a different theoretical model of compilation to be avoided, whereas others are basically just made to avoid mutating the original AST. Some of this just boils down to not having a const-first attitude, other things are harder. Making more things const makes avoiding these copies easier. I was fiddling around with a dmd patch that automatically spits out a diff that adds const to things where it can.
 Looking at a profiler, how would you say these cycles are 
 distributed between "copy tree" and "walk tree for semantic"?
A *lot* of dmd compilation is spent doing memcpy. However I think it's actually mostly caused by CTFE arrays being shunted around. The thing to do for those arrays is probably to refcount them so the copy on write can be done in place for the hopefully common case.
Jan 16 2023
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/16/23 4:12 AM, FeepingCreature wrote:
 On Monday, 16 January 2023 at 04:30:25 UTC, Steven Schveighoffer wrote:
 In this post: https://forum.dlang.org/post/tm3p0p$2js2$1 digitalmars.com

 I mentioned:

 I did a test on something I was working on for my talk, and I'm going 
 to write a blog post about it, because I'm kind of stunned at the 
 results.
Well, I finally got around to it: https://www.schveiguy.com/blog/2023/01/the-cost-of-compile-time-in-d/ Let me know what you think.
Looks like :handwaves: given a 2.4Ghz processor, that'd be 150k cycles per ReturnType instantiation? Not super much, but not nothing either. If that distributes over five templates, it'd be something like 30k for a template instantiation in general. For something that hits the allocator a few times, that seems ... about right? Indicating to me that if we want this to be fast, we have to find a way to make a template instantiation *do less.* I think that's gonna be a hard sell, given that the instantiation absolutely has to make a copy of the entire template body AST given the compiler design as it is.
Absolutely, I welcome any improvements that bring the current phobos into line with the improved version. I would imagine *some* penalty for ReturnType, regardless of how much can be improved. And of course, there's the whole question of "is this the right abstraction to use?". Would there be a better way to write ReturnType that doesn't cost as much, maybe using CTFE? I don't know enough about the actual implementation, so its hard for me to have a productive discussion on it. All I can do is try things and measure. -Steve
Jan 16 2023
prev sibling next sibling parent Hipreme <msnmancini hotmail.com> writes:
On Monday, 16 January 2023 at 04:30:25 UTC, Steven Schveighoffer 
wrote:
 In this post: 
 https://forum.dlang.org/post/tm3p0p$2js2$1 digitalmars.com

 I mentioned:

 I did a test on something I was working on for my talk, and 
 I'm going to write a blog post about it, because I'm kind of 
 stunned at the results.
Well, I finally got around to it: https://www.schveiguy.com/blog/2023/01/the-cost-of-compile-time-in-d/ Let me know what you think. -Steve
Pretty interesting this post! Although that really makes me sad because it only shows we can't really create helpers or use D effectively. I wish we could create a better thing for that.
Jan 16 2023
prev sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sun, Jan 15, 2023 at 11:30:25PM -0500, Steven Schveighoffer via
Digitalmars-d wrote:
[...]
 https://www.schveiguy.com/blog/2023/01/the-cost-of-compile-time-in-d/
[...] Honestly, I find templates like ReturnType in std.traits a bit of a code smell. Same thing as Parameters, and a whole bunch of others. Yes, it has a pretty-sounding name, and yes, you get to avoid writing that squint-inducing __traits(...) syntax, but if you take a step back, it just begs the question, why can't we do this directly in the language itself? You pointed out that there are various reasons for it -- no easy way of getting an instance of the type, need to handle different kinds of callables, etc., but to me, those are all merely circumstantial issues. It begs the question, why *isn't* there a construct to obtain an instance of some type T (even if hypothetical, for the purposes of introspection)? After all, the compiler knows T inside-out, and ought to be able to cook up a (virtual) instance of it. The crux of the problem is that the in spite of D's metaprogramming prowess being often promoted, the language *itself* doesn't let you do certain common things easily. It lets you use, e.g., typeof() in certain cases, but in other cases you need this or that workaround or paraphrasis, and so another std.traits wrapper template is born. If the language had instead been extended so that you could, for example, extract the return type of some given callable directly, say typeof(return(myfunc)), then none of this would have been necessary in the first place. Having wrappers in Phobos for doing certain things makes sense when a particular feature or introspective capability is still new / newly discovered: it wasn't anticipated so the language doesn't have a construct to express it directly, so a Phobos template helps to wrap it up in a nicer, more friendly and usable form for end users to use. But once a particular construct has become recurrent and a standard part of D idiom, it deserves to be baked into the language directly. Especially when doing so eliminates a lot of the collateral costs. T -- What's an anagram of "BANACH-TARSKI"? BANACH-TARSKI BANACH-TARSKI.
Jan 17 2023
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/17/2023 1:58 PM, H. S. Teoh wrote:
 If the
 language had instead been extended so that you could, for example,
 extract the return type of some given callable directly, say
 typeof(return(myfunc)), then none of this would have been necessary in
 the first place.
https://dlang.org/spec/expression.html#is_expression int func(); static if (is(typeof(func) R == return)) pragma(msg, R); prints: int The implementation of std.traits.ReturnType is: template ReturnType(alias func) if (isCallable!func) { static if (is(FunctionTypeOf!func R == return)) alias ReturnType = R; else static assert(0, "argument has no return type"); } ReturnType can do a little more than the raw IsExpression, as it can identify: struct G { int opCall (int i) { return 1;} }
Jan 18 2023
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/18/23 5:20 PM, Walter Bright wrote:
 On 1/17/2023 1:58 PM, H. S. Teoh wrote:
 If the
 language had instead been extended so that you could, for example,
 extract the return type of some given callable directly, say
 typeof(return(myfunc)), then none of this would have been necessary in
 the first place.
https://dlang.org/spec/expression.html#is_expression     int func();     static if (is(typeof(func) R == return))         pragma(msg, R); prints:     int The implementation of std.traits.ReturnType is:     template ReturnType(alias func)     if (isCallable!func)     {         static if (is(FunctionTypeOf!func R == return))             alias ReturnType = R;         else             static assert(0, "argument has no return type");     } ReturnType can do a little more than the raw IsExpression, as it can identify:     struct G     {         int opCall (int i) { return 1;}     }
I didn't think of making ReturnType simplified (we know in this case the thing is not anything except a normal lambda function). I did this now: ```d template RT(alias sym) { static if(is(typeof(sym) R == return)) alias RT = R; else static assert(false, "bad"); } ... else version(useIsExpr) { enum isInputRange(R) = is(typeof(R.init) == R) && is(RT!((R r) => r.empty) == bool) && (is(typeof((return ref R r) => r.front)) || is(typeof(ref (return ref R r) => r.front))) && !is(RT!((R r) => r.front) == void) && is(typeof((R r) => r.popFront)); } ``` The result is still not as good as just using typeof directly, but much much better. When compared to a direct typeof, it adds about 0.15s of total compile time for 10000 instances, and adds 100MB more memory usage. My point still stands -- *inside* a constraint template, you should avoid using all kinds of convenience templates if you can help it. Nobody cares about the implementation of `isInputRange`, as long as it gives the right answer. Now, Adam has a point (in his comment on my blog) that if you are *already* using such convenience templates elsewhere on the *same parameters*, then this can have a negative effect on overall performance, because the caching of the template answer can speed up the compilation. In this case, the template instantiation is guaranteed to be unique since these are lambda expressions, so that doesn't apply. I think we can all agree though that it is less than ideal to have to worry about the internal details of how templates are implemented. -Steve
Jan 19 2023
parent reply Commander Zot <no no.no> writes:
On Thursday, 19 January 2023 at 15:19:06 UTC, Steven 
Schveighoffer wrote:
 On 1/18/23 5:20 PM, Walter Bright wrote:
 On 1/17/2023 1:58 PM, H. S. Teoh wrote:
 I think we can all agree though that it is less than ideal to 
 have to worry about the internal details of how templates are 
 implemented.

 -Steve
i disagree with this. - it makes the library harder to maintain. - it hides the actual problem. - people writing their own code will still be left with the problem.
Jan 19 2023
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/19/23 11:22 AM, Commander Zot wrote:
 On Thursday, 19 January 2023 at 15:19:06 UTC, Steven Schveighoffer wrote:
 On 1/18/23 5:20 PM, Walter Bright wrote:
 On 1/17/2023 1:58 PM, H. S. Teoh wrote:
 I think we can all agree though that it is less than ideal to have to 
 worry about the internal details of how templates are implemented.
i disagree with this. - it makes the library harder to maintain. - it hides the actual problem. - people writing their own code will still be left with the problem.
I'm not sure I understand your disagreement with the quote. I think we are saying the same thing? -Steve
Jan 19 2023
parent reply Commander Zot <no no.no> writes:
On Thursday, 19 January 2023 at 16:39:17 UTC, Steven 
Schveighoffer wrote:
 On 1/19/23 11:22 AM, Commander Zot wrote:
 On Thursday, 19 January 2023 at 15:19:06 UTC, Steven 
 Schveighoffer wrote:
 On 1/18/23 5:20 PM, Walter Bright wrote:
 On 1/17/2023 1:58 PM, H. S. Teoh wrote:
 I think we can all agree though that it is less than ideal to 
 have to worry about the internal details of how templates are 
 implemented.
i disagree with this. - it makes the library harder to maintain. - it hides the actual problem. - people writing their own code will still be left with the problem.
I'm not sure I understand your disagreement with the quote. I think we are saying the same thing? -Steve
maybe i misunderstand your point, but let me express my thoughts a bit more: we should worry about the internal details of how templates are implemented. they should be implemented in the most idiomatic way. manually optimising the code and making it harder to understand is suboptimal. and doing it won't fix the same problem in user code. it really just hides it.
Jan 19 2023
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/19/23 11:56 AM, Commander Zot wrote:
 On Thursday, 19 January 2023 at 16:39:17 UTC, Steven Schveighoffer wrote:
 On 1/19/23 11:22 AM, Commander Zot wrote:
 On Thursday, 19 January 2023 at 15:19:06 UTC, Steven Schveighoffer 
 wrote:

 I think we can all agree though that it is less than ideal to have 
 to worry about the internal details of how templates are implemented.
i disagree with this. - it makes the library harder to maintain. - it hides the actual problem. - people writing their own code will still be left with the problem.
I'm not sure I understand your disagreement with the quote. I think we are saying the same thing?
maybe i misunderstand your point, but let me express my thoughts a bit more: we should worry about the internal details of how templates are implemented. they should be implemented in the most idiomatic way.
Yes, this is what I said "less than ideal to have to worry about the internal details". Ideally, we should be able to use as much templates as we want, and pay a minimal cost. Now, to be sure, we are going to pay a cost. It just shouldn't be so expensive that it causes abandonment of D for it (some recent examples noted on these forums). Making Phobos constraint templates less costly is a worthy goal, whether it's done via reducing the cost of templates overall, or finding less expensive ways to implement them. Worth noting that the compiler can't just be completely magic, sometimes you do have to rearrange things to get it to work better. -Steve
Jan 19 2023