digitalmars.D - Overloading Lazy Vs. Non-Lazy
- dsimcha <dsimcha yahoo.com> Aug 11 2010
- bearophile <bearophileHUGS lycos.com> Aug 11 2010
- "Steven Schveighoffer" <schveiguy yahoo.com> Aug 11 2010
- Tomek =?UTF-8?B?U293acWEc2tp?= <just ask.me> Aug 11 2010
- Brad Roberts <braddr puremagic.com> Aug 11 2010
- Don <nospam nospam.com> Aug 12 2010
- dsimcha <dsimcha yahoo.com> Aug 12 2010
- Jonathan M Davis <jmdavisprog gmail.com> Aug 12 2010
- "Steven Schveighoffer" <schveiguy yahoo.com> Aug 12 2010
- "Steven Schveighoffer" <schveiguy yahoo.com> Aug 12 2010
- "Steven Schveighoffer" <schveiguy yahoo.com> Aug 13 2010
- "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> Aug 13 2010
An issue that's come up here several times before is that enforce() effectively disables inlining of the function it's used in. From reading some disassemblies, the reason seems to be because of all the ASM code that's required for lazy parameters. I wonder if either of the following is feasible: 1. When a function takes a lazy parameter, the compiler automatically generates two versions under the hood: One that actually takes a non-lazy parameter and is used when the value is known at compile time and another that works like current lazy functions. The only problem here is that this might create issues when using function pointers/delegates. 2. Allow overloading of lazy and non-lazy functions, with the rule that the lazy version gets called whenever the value must be computed at runtime and the non-lazy version gets called if the value is statically known and thus there's no evaluation to speak of.
Aug 11 2010
dsimcha:An issue that's come up here several times before is that enforce() effectively disables inlining of the function it's used in.
This is a problem because many Phobos functions, even small ones, are sprinkled with enforce(). If the purpose of enforce() is just to stop the program/thread even in release mode, then it may be replaced by something light and simpler, that doesn't stop the inliner. Bye, bearophile
Aug 11 2010
On Wed, 11 Aug 2010 09:19:01 -0400, dsimcha <dsimcha yahoo.com> wrote:An issue that's come up here several times before is that enforce() effectively disables inlining of the function it's used in. From reading some disassemblies, the reason seems to be because of all the ASM code that's required for lazy parameters. I wonder if either of the following is feasible: 1. When a function takes a lazy parameter, the compiler automatically generates two versions under the hood: One that actually takes a non-lazy parameter and is used when the value is known at compile time and another that works like current lazy functions. The only problem here is that this might create issues when using function pointers/delegates. 2. Allow overloading of lazy and non-lazy functions, with the rule that the lazy version gets called whenever the value must be computed at runtime and the non-lazy version gets called if the value is statically known and thus there's no evaluation to speak of.
Hold on, can't we have enforce and lenforce (lazy enforce)? From a simple grep, 99% of enforce instances are: enforce(condition) -or- enforce(condition, "compile-time-string") -or- enforce(condition, "compile-time-string" ~ type.stringof) There are a few ones which actually would be allocating strings at runtime, but seriously, can't we just modify those to lenforce and get inlining everywhere else? I think phobos would see both a huge speedup and a smaller footprint immediately if enforce did not take a lazy arg. -Steve
Aug 11 2010
Steven Schveighoffer napisał:Hold on, can't we have enforce and lenforce (lazy enforce)? From a simple grep, 99% of enforce instances are: enforce(condition) -or- enforce(condition, "compile-time-string") -or- enforce(condition, "compile-time-string" ~ type.stringof) There are a few ones which actually would be allocating strings at runtime, but seriously, can't we just modify those to lenforce and get inlining everywhere else? I think phobos would see both a huge speedup and a smaller footprint immediately if enforce did not take a lazy arg.
I like this idea. But > 1 option makes you think and that's painful;) BTW, am I the only person surprised that in a language whose generic code to fit the need can transform like Optimus Prime himself you can't do lazy <-> non-lazy automagically? Tomek
Aug 11 2010
On 8/11/2010 6:19 AM, dsimcha wrote:An issue that's come up here several times before is that enforce() effectively disables inlining of the function it's used in. From reading some disassemblies, the reason seems to be because of all the ASM code that's required for lazy parameters. I wonder if either of the following is feasible: 1. When a function takes a lazy parameter, the compiler automatically generates two versions under the hood: One that actually takes a non-lazy parameter and is used when the value is known at compile time and another that works like current lazy functions. The only problem here is that this might create issues when using function pointers/delegates. 2. Allow overloading of lazy and non-lazy functions, with the rule that the lazy version gets called whenever the value must be computed at runtime and the non-lazy version gets called if the value is statically known and thus there's no evaluation to speak of.
It's the throw that blocks inlining right now. Lazy might also disable inlining too, I haven't looked for that case. Either way, that's purely a quality of implementation issue. I don't think it'd be a good idea to bend the library all that much to work around that kind of limitation. It's something that will get better as time permits. Later, Brad
Aug 11 2010
Steven Schveighoffer wrote:On Thu, 12 Aug 2010 02:00:00 -0400, Brad Roberts <braddr puremagic.com> wrote:On 8/11/2010 6:19 AM, dsimcha wrote:An issue that's come up here several times before is that enforce() effectively disables inlining of the function it's used in. From reading some disassemblies, the reason seems to be because of all the ASM code that's required for lazy parameters. I wonder if either of the following is feasible: 1. When a function takes a lazy parameter, the compiler automatically generates two versions under the hood: One that actually takes a non-lazy parameter and is used when the value is known at compile time and another that works like current lazy functions. The only problem here is that this might create issues when using function pointers/delegates. 2. Allow overloading of lazy and non-lazy functions, with the rule that the lazy version gets called whenever the value must be computed at runtime and the non-lazy version gets called if the value is statically known and thus there's no evaluation to speak of.
It's the throw that blocks inlining right now. Lazy might also disable inlining too, I haven't looked for that case. Either way, that's purely a quality of implementation issue. I don't think it'd be a good idea to bend the library all that much to work around that kind of limitation. It's something that will get better as time permits.
Well, there's something to be said about preventing the bloated generation of code that accompanies lazy.
Inlining a lazy function that only returns a compile-time constant could inline very nicely. The delegate would disappear completely.But throwing preventing inlining is a bad one, that should be fixed. -Steve
Aug 12 2010
== Quote from Don (nospam nospam.com)'s articleSteven Schveighoffer wrote:On Thu, 12 Aug 2010 02:00:00 -0400, Brad Roberts <braddr puremagic.com> wrote:On 8/11/2010 6:19 AM, dsimcha wrote:An issue that's come up here several times before is that enforce() effectively disables inlining of the function it's used in. From reading some disassemblies, the reason seems to be because of all the ASM code that's required for lazy parameters. I wonder if either of the following is feasible: 1. When a function takes a lazy parameter, the compiler automatically generates two versions under the hood: One that actually takes a non-lazy parameter and is used when the value is known at compile time and another that works like current lazy functions. The only problem here is that this might create issues when using function pointers/delegates. 2. Allow overloading of lazy and non-lazy functions, with the rule that the lazy version gets called whenever the value must be computed at runtime and the non-lazy version gets called if the value is statically known and thus there's no evaluation to speak of.
It's the throw that blocks inlining right now. Lazy might also disable inlining too, I haven't looked for that case. Either way, that's purely a quality of implementation issue. I don't think it'd be a good idea to bend the library all that much to work around that kind of limitation. It's something that will get better as time permits.
Well, there's something to be said about preventing the bloated generation of code that accompanies lazy.
inline very nicely. The delegate would disappear completely.
Just to clarify: enforce() calls a function called bailOut() if necessary that actually does the throwing. This of course is not inlined w.r.t. enforce(). However, the lazy parameter also prevents inlining of enforce() itself and generates a ton of code at the ASM level. Don is right, though. In principle, when a statically known delegate D (lazy is just syntactic sugar for delegates) that returns a statically known constant is passed to a statically known function F, D can be inlined w.r.t. F and F can be inlined w.r.t. its caller. The problem, though, is that F needs to be inlined first so that the compiler can optimize F w.r.t. the parameters passed to it from a specific caller. However, F won't look worth inlining until the compiler realizes that it can inline D w.r.t. F. Therefore, the compiler would need an inliner that uses something more sophisticated than a simple greedy algorithm to discover this optimization opportunity.
Aug 12 2010
On Thursday, August 12, 2010 13:47:40 dsimcha wrote:Just to clarify: enforce() calls a function called bailOut() if necessary that actually does the throwing. This of course is not inlined w.r.t. enforce(). However, the lazy parameter also prevents inlining of enforce() itself and generates a ton of code at the ASM level. Don is right, though. In principle, when a statically known delegate D (lazy is just syntactic sugar for delegates) that returns a statically known constant is passed to a statically known function F, D can be inlined w.r.t. F and F can be inlined w.r.t. its caller. The problem, though, is that F needs to be inlined first so that the compiler can optimize F w.r.t. the parameters passed to it from a specific caller. However, F won't look worth inlining until the compiler realizes that it can inline D w.r.t. F. Therefore, the compiler would need an inliner that uses something more sophisticated than a simple greedy algorithm to discover this optimization opportunity.
Well, from everything I've heard about the inliner, we're going to need a smarter one at some point. Whether we need one soon is another matter, but at some point, we really should have an inliner capable of handling these sort of cases. It sounds like dmd's inliner is pretty simplistic, and that's going to make it harder to make stuff like Phobos properly efficient. - Jonathan M Davis
Aug 12 2010
On Thu, 12 Aug 2010 02:00:00 -0400, Brad Roberts <braddr puremagic.com> wrote:On 8/11/2010 6:19 AM, dsimcha wrote:An issue that's come up here several times before is that enforce() effectively disables inlining of the function it's used in. From reading some disassemblies, the reason seems to be because of all the ASM code that's required for lazy parameters. I wonder if either of the following is feasible: 1. When a function takes a lazy parameter, the compiler automatically generates two versions under the hood: One that actually takes a non-lazy parameter and is used when the value is known at compile time and another that works like current lazy functions. The only problem here is that this might create issues when using function pointers/delegates. 2. Allow overloading of lazy and non-lazy functions, with the rule that the lazy version gets called whenever the value must be computed at runtime and the non-lazy version gets called if the value is statically known and thus there's no evaluation to speak of.
It's the throw that blocks inlining right now. Lazy might also disable inlining too, I haven't looked for that case. Either way, that's purely a quality of implementation issue. I don't think it'd be a good idea to bend the library all that much to work around that kind of limitation. It's something that will get better as time permits.
Well, there's something to be said about preventing the bloated generation of code that accompanies lazy. But throwing preventing inlining is a bad one, that should be fixed. -Steve
Aug 12 2010
On Wed, 11 Aug 2010 18:10:53 -0400, Tomek Sowiński <just ask.me> wrote:Steven Schveighoffer napisał:Hold on, can't we have enforce and lenforce (lazy enforce)? From a simple grep, 99% of enforce instances are: enforce(condition) -or- enforce(condition, "compile-time-string") -or- enforce(condition, "compile-time-string" ~ type.stringof) There are a few ones which actually would be allocating strings at runtime, but seriously, can't we just modify those to lenforce and get inlining everywhere else? I think phobos would see both a huge speedup and a smaller footprint immediately if enforce did not take a lazy arg.
I like this idea. But > 1 option makes you think and that's painful;)
One possibility is to have enforce force you to pass in a compile-time evaluatable string. I don't really know if this is possible. But then at least the compiler could prevent you from accidentally using the wrong enforce.BTW, am I the only person surprised that in a language whose generic code to fit the need can transform like Optimus Prime himself you can't do lazy <-> non-lazy automagically?
It's been suggested before (by me even): http://d.puremagic.com/issues/show_bug.cgi?id=1817 But that would be limited to templates only. A non-template function which takes a lazy argument could not make any conversions. I'd rather see an overload, or just separate the names. -Steve
Aug 12 2010
On Thu, 12 Aug 2010 16:47:40 -0400, dsimcha <dsimcha yahoo.com> wrote:== Quote from Don (nospam nospam.com)'s articleSteven Schveighoffer wrote:On Thu, 12 Aug 2010 02:00:00 -0400, Brad Roberts
wrote:On 8/11/2010 6:19 AM, dsimcha wrote:An issue that's come up here several times before is that enforce() effectively disables inlining of the function it's used in. From reading some disassemblies, the reason seems to be because of all the ASM code
required for lazy parameters. I wonder if either of the following
feasible: 1. When a function takes a lazy parameter, the compiler
generates two versions under the hood: One that actually takes a non-lazy parameter and is used when the value is known at compile time and another that works like current lazy functions. The only problem here is that this might create issues when using function pointers/delegates. 2. Allow overloading of lazy and non-lazy functions, with the rule that the lazy version gets called whenever the value must be computed at runtime and the non-lazy version gets called if the value is statically known
thus there's no evaluation to speak of.
It's the throw that blocks inlining right now. Lazy might also disable inlining too, I haven't looked for that case. Either way, that's purely a quality of implementation issue. I don't think it'd be a good idea to bend the library all that much to work around that kind of limitation. It's something
will get better as time permits.
Well, there's something to be said about preventing the bloated generation of code that accompanies lazy.
inline very nicely. The delegate would disappear completely.
Just to clarify: enforce() calls a function called bailOut() if necessary that actually does the throwing. This of course is not inlined w.r.t. enforce(). However, the lazy parameter also prevents inlining of enforce() itself and generates a ton of code at the ASM level. Don is right, though. In principle, when a statically known delegate D (lazy is just syntactic sugar for delegates) that returns a statically known constant is passed to a statically known function F, D can be inlined w.r.t. F and F can be inlined w.r.t. its caller. The problem, though, is that F needs to be inlined first so that the compiler can optimize F w.r.t. the parameters passed to it from a specific caller. However, F won't look worth inlining until the compiler realizes that it can inline D w.r.t. F. Therefore, the compiler would need an inliner that uses something more sophisticated than a simple greedy algorithm to discover this optimization opportunity.
In principle, we should be able to inline enforce. Great. When is this going to happen? Well, we can inline all calls to enforce *today* if we simply remove the lazy attribute, and apply a lazy version in a small number of places. I think it's worth doing it, because the inlining fixes are not going to happen for a while. It's easy to switch it back once we can inline trivial lazy parameters. -Steve
Aug 13 2010
On Fri, 13 Aug 2010 09:21:00 -0400, Steven Schveighoffer wrote:On Thu, 12 Aug 2010 16:47:40 -0400, dsimcha <dsimcha yahoo.com> wrote:== Quote from Don (nospam nospam.com)'s articleSteven Schveighoffer wrote:On Thu, 12 Aug 2010 02:00:00 -0400, Brad Roberts
wrote:On 8/11/2010 6:19 AM, dsimcha wrote:An issue that's come up here several times before is that enforce() effectively disables inlining of the function it's used in. From reading some disassemblies, the reason seems to be because of all the ASM code
required for lazy parameters. I wonder if either of the following
feasible: 1. When a function takes a lazy parameter, the compiler
generates two versions under the hood: One that actually takes a non-lazy parameter and is used when the value is known at compile time and another that works like current lazy functions. The only problem here is that this might create issues when using function pointers/delegates. 2. Allow overloading of lazy and non-lazy functions, with the rule that the lazy version gets called whenever the value must be computed at runtime and the non-lazy version gets called if the value is statically known
thus there's no evaluation to speak of.
It's the throw that blocks inlining right now. Lazy might also disable inlining too, I haven't looked for that case. Either way, that's purely a quality of implementation issue. I don't think it'd be a good idea to bend the library all that much to work around that kind of limitation. It's something
will get better as time permits.
Well, there's something to be said about preventing the bloated generation of code that accompanies lazy.
could inline very nicely. The delegate would disappear completely.
Just to clarify: enforce() calls a function called bailOut() if necessary that actually does the throwing. This of course is not inlined w.r.t. enforce(). However, the lazy parameter also prevents inlining of enforce() itself and generates a ton of code at the ASM level. Don is right, though. In principle, when a statically known delegate D (lazy is just syntactic sugar for delegates) that returns a statically known constant is passed to a statically known function F, D can be inlined w.r.t. F and F can be inlined w.r.t. its caller. The problem, though, is that F needs to be inlined first so that the compiler can optimize F w.r.t. the parameters passed to it from a specific caller. However, F won't look worth inlining until the compiler realizes that it can inline D w.r.t. F. Therefore, the compiler would need an inliner that uses something more sophisticated than a simple greedy algorithm to discover this optimization opportunity.
In principle, we should be able to inline enforce. Great. When is this going to happen? Well, we can inline all calls to enforce *today* if we simply remove the lazy attribute, and apply a lazy version in a small number of places. I think it's worth doing it, because the inlining fixes are not going to happen for a while. It's easy to switch it back once we can inline trivial lazy parameters. -Steve
I agree. A quick grep of the phobos sources shows that in the vast majority of cases, enforce() gets called either without a message or just with a string literal. -Lars
Aug 13 2010









bearophile <bearophileHUGS lycos.com> 