Re: Should we add `a * b` for vectors?
On Thursday, 28 September 2017 at 01:58:24 UTC, Walter Bright wrote: On 9/27/2017 4:21 PM, Manu wrote: [...] but I see no advantage to it over D's approach (the reverse operand lookup scheme). I couldn't find anything focused on D's overloading resolution scheme (or anything with the specific term "reverse operand lookup"). Can you elaborate?
Just playing with compiler explorer to see assembly line count.
//D compiled with gdc 5.2 -O3 auto test(int[] arr, int cmp) { int[] r; foreach(v ; arr) if(v == cmp)r~=v; return r; } // 51 lines of assembly auto test(int[] arr, int cmp) { return arr.filter!((v)=>v==cmp).array; } //1450 lines... what? Ok let me look also at c++: //gcc 7.2 -O3 vector test(vector& arr, int cmp) { vector r; for(auto v : arr) if(v == cmp)r.push_back(v); return r; } //152 lines. more than D :) vector test(vector& arr, int cmp) { vector r; std::copy_if (arr.begin(), arr.end(), std::back_inserter(r), [cmp](int i){return i==cmp;} ); return r; } //150 lines. That what i expected earlier with D. Hmm. let me be 'fair' and use std.container.array just for curiosity: auto test(ref Array!int arr, int cmp) { Array!int r; foreach(v ; arr) if(v == cmp)r.insert(v); return r; } //5542 lines... what?? Someone interested to discuss about this? Or point me some grotesque mistake.
Re: Just playing with compiler explorer to see assembly line count.
Be warned, x86 cpu's today are not like they were 10 years ago. A good portion of a symbol could be full of nop's and it could end up being faster than the one without them. Next, compare against ldc, not gdc primarily. Its better maintained and ugh more inline with dmd (its a bit of a mess, lets not go there). Of course nothing wrong with doing both. std.container.* is basically dead. We need to replace it. We are currently waiting on std.experimental.allocators before going much more further (also a lot of other no-gc stuff). Compare (on https://d.godbolt.org/ with "ldc -O3" and "gdc -O3"): --- auto test1(int[] arr, int cmp) { int[] r; foreach(v ; arr) if(v == cmp)r~=v; return r; } import std.container.array; auto test2(ref Array!int arr, int cmp) { Array!int r; foreach(v ; arr) if(v == cmp)r.insert(v); return r; } ---
Re: Just playing with compiler explorer to see assembly line count.
On Tuesday, 3 October 2017 at 13:53:38 UTC, rikki cattermole wrote: Be warned, x86 cpu's today are not like they were 10 years ago. A good portion of a symbol could be full of nop's and it could end up being faster than the one without them. Next, compare against ldc, not gdc primarily. Its better maintained and ugh more inline with dmd (its a bit of a mess, lets not go there). Of course nothing wrong with doing both. std.container.* is basically dead. We need to replace it. We are currently waiting on std.experimental.allocators before going much more further (also a lot of other no-gc stuff). Compare (on https://d.godbolt.org/ with "ldc -O3" and "gdc -O3"): --- auto test1(int[] arr, int cmp) { int[] r; foreach(v ; arr) if(v == cmp)r~=v; return r; } import std.container.array; auto test2(ref Array!int arr, int cmp) { Array!int r; foreach(v ; arr) if(v == cmp)r.insert(v); return r; } --- With ldc the results are similar. 5k+ And I know, im not into performance comparison yet. But you know, less code, more cache friendly (and sometimes better performance). But my big surprise was with .filter.
Re: Just playing with compiler explorer to see assembly line count.
On Tuesday, 3 October 2017 at 14:07:39 UTC, SrMordred wrote: On Tuesday, 3 October 2017 at 13:53:38 UTC, rikki cattermole wrote: Be warned, x86 cpu's today are not like they were 10 years ago. A good portion of a symbol could be full of nop's and it could end up being faster than the one without them. Next, compare against ldc, not gdc primarily. Its better maintained and ugh more inline with dmd (its a bit of a mess, lets not go there). Of course nothing wrong with doing both. std.container.* is basically dead. We need to replace it. We are currently waiting on std.experimental.allocators before going much more further (also a lot of other no-gc stuff). Compare (on https://d.godbolt.org/ with "ldc -O3" and "gdc -O3"): --- auto test1(int[] arr, int cmp) { int[] r; foreach(v ; arr) if(v == cmp)r~=v; return r; } import std.container.array; auto test2(ref Array!int arr, int cmp) { Array!int r; foreach(v ; arr) if(v == cmp)r.insert(v); return r; } --- With ldc the results are similar. 5k+ And I know, im not into performance comparison yet. But you know, less code, more cache friendly (and sometimes better performance). Well -O3 does not generate cache friendly assembly anyway. For instance, inlining functions regardless of cost. I'd take the assembly count with a pinch of salt when you are using templated code. Iain
Re: Alternatives to extern(delegate) (DIP 1011)
On Monday, 2 October 2017 at 07:48:54 UTC, Jonathan Marler wrote: Andrei suggests we take a closer look at alternatives to DIP 1011 (extern(delegate)) that can be implemented in a library. In the past I've failed to come up with anything good, however, my last attempt seemed to be less horrible than before. I'm inviting anyone to view/modify my feeble attempt at a solution here https://github.com/marler8997/externdelegate A good library solution would be great but my current solution has problems. It doesn't really support template parameters yet and I'm not sure if it can be solved in the general case without pulling in a fair chunk of the language grammar into the library. Assuming that can be fixed, the bigger concern is that the API to define a "delegate function" looks very awkward. It provides a template mixin that you can use to create a delegate function by passing in your function definition as a string. With the current solution I believe its very unlikely that applications would use the library. Keep in mind that this feature is something that a library could use on most or all of it's functions. If it has to use such an awkward syntax it's highly unlikely it would be used at that scale. Not to mention that when you start pushing the limits of the language like this you almost always run into bugs and end up having to go with a different solution. Also I haven't measured it but I'm almost certain this would greatly increase compile time as more functions start using it. I've improved the solution. I've gotten templates to work, however, template parameter deduction won't work because I'm currently defining the delegate functions as structs. I'm including a bit of code to demonstrate the solution. The example will demonstrate how to define a "delegate function" that uses a File object for it's context. The function is called "importantWriteln" which simply calls writeln with a prefix of "Important: ". Here's how you would define that function: mixin delegateFunctionImpl!("importantWriteln", "(T...)", "File", "file", "T args", q{ file.writeln("Important: ", args); }); This is the code that the mixin would generate: struct importantWriteln(T...) { File file; auto opCall(T args) { file.writeln("Important: ", args); } pragma(inline) static auto opCall(ref File file, T args) { return (cast(importantWriteln*)&file).opCall(args); } // NOTE: this doesn't quite work yet if there are template parameters pragma(inline) static auto createDelegate(T...)(ref File file) { return &(cast(importantWriteln*)&file).opCall; } } And here's how you would use it: // call like a normal function importantWriteln!(string,string)(stdout, "Hello, ", "World!"); // NOTE: template type deduction doesn't work since importantWriteln is a struct // call like a delegate auto dg = importantWriteln!(string,string).createDelegate(stdout); dg("Hello, ", "again"); The worst part about this solution is how you define the function. The way I evaluate the solution is I think about ALL the functions in phobos that could use this feature (most functions that would normally be called using UFCS are good candidates) then think about whether I could create a pull request to make them all delegate functions. Given this solution, I don't think that it would be accepted.
Re: Just playing with compiler explorer to see assembly line count.
is not bad https://godbolt.org/g/bSfubs On Tue, Oct 3, 2017 at 3:19 PM, SrMordred via Digitalmars-d < digitalmars-d@puremagic.com> wrote: > //D compiled with gdc 5.2 -O3 > > auto test(int[] arr, int cmp) > { > int[] r; > foreach(v ; arr) > if(v == cmp)r~=v; > return r; > } > // 51 lines of assembly > > auto test(int[] arr, int cmp) > { > return arr.filter!((v)=>v==cmp).array; > } > //1450 lines... what? > > Ok let me look also at c++: > //gcc 7.2 -O3 > > vector test(vector& arr, int cmp) { > vector r; > for(auto v : arr) > if(v == cmp)r.push_back(v); > return r; > } > //152 lines. more than D :) > > vector test(vector& arr, int cmp) { > vector r; > std::copy_if (arr.begin(), arr.end(), std::back_inserter(r), > [cmp](int i){return i==cmp;} ); > return r; > } > > //150 lines. That what i expected earlier with D. > > Hmm. let me be 'fair' and use std.container.array just for curiosity: > > auto test(ref Array!int arr, int cmp) > { > Array!int r; > foreach(v ; arr) > if(v == cmp)r.insert(v); > return r; > } > > //5542 lines... what?? > > Someone interested to discuss about this? > > Or point me some grotesque mistake. >
Re: Just playing with compiler explorer to see assembly line count.
On Tuesday, 3 October 2017 at 17:15:04 UTC, Daniel Kozak wrote: is not bad https://godbolt.org/g/bSfubs Thats cool, I never used copy xD. (but you returned the .copy range, not the 'r' array ;p) //now with ldc 1.4 and -O3 -release -boundscheck=off foreach -> 99 lines .filter.copy -> 368 lines .filter.array -> 1229 lines (1002 lines with -O1)
Re: Should we add `a * b` for vectors?
On 10/2/2017 4:15 AM, Timon Gehr wrote: On 30.09.2017 23:45, Walter Bright wrote: ... D has other ways of doing what ADL does, What are those ways? I'm aware of two basic strategies, both suboptimal: - Every module imports all other modules. - Proliferation of wrapper types. https://dlang.org/spec/operatoroverloading.html#binary C++ does not have this notion. It's not per se related to operator overloading: ADL was specifically intended to address operator overloading. --- module a; import std.range: isInputRange; auto sum(R)(R r)if(isInputRange!R){ typeof(r.front) result; for(auto t=r.save;!t.empty;t.popFront()) result+=t.front; return result; } --- --- module b; import a; import std.range; void main(){ int[] a = [1,2,3,4]; import std.stdio: writeln; writeln(a.front); // ok writeln(sum(a)); // error, the type is an input range, yet has no front } --- I.e., the operations that are supported on the type differ depending on the module that it is accessed from. If there are multiple definitions of the same name, different modules might not agree which one is being referred to. (This is particularly likely for overloaded operators, as the set of names is finite and small. This is what Manu means when he says it can lead to nasty surprises. This is very plausible, but I don't have a good example.) This gets into the anti-hijacking support D has (and C++ does not). If multiple modules with declarations of `writeln` are in scope, the compiler attempts a match with `writeln` in each module. If there is a match with more than one module, an error is issued. The user will then have to specify which one he wants. This is specifically designed to prevent nasty surprises. C++ has a big problem with ADL in that overloading is not encapsulated and any #included header can inadvertently add more overloads that may be entirely unrelated. Any .h can crack open any namespace and insert more overloads into it. It's completely unhygienic and uncontrollable. As for the specific example you gave, I get: a.d(3): Error: no property 'front' for type 'int[]' a.d(4): Error: no property 'save' for type 'int[]' b.d(8): Error: template instance a.sum!(int[]) error instantiating
Re: Should we add `a * b` for vectors?
On 10/3/2017 12:58 AM, Enamex wrote: I couldn't find anything focused on D's overloading resolution scheme (or anything with the specific term "reverse operand lookup"). Can you elaborate? See my reply to Timon.
Re: Should we add `a * b` for vectors?
On Tuesday, 3 October 2017 at 19:25:32 UTC, Walter Bright wrote: This gets into the anti-hijacking support D has (and C++ does not). If multiple modules with declarations of `writeln` are in scope, the compiler attempts a match with `writeln` in each module. If there is a match with more than one module, an error is issued. The user will then have to specify which one he wants. Anti-hacking might make it tricky to have operators as free-standing functions, but I'm not sure it would be impossible. The solution for function overloading is fully qualified names. However, this does not make sense operator overloading. Mixing operator overloading and D's anti-hijacking, you would basically be prevented from having two overloaded versions of an operator. This means that if you want multiple versions of operators, you would not be able to have the operators as member functions and they would have to be free-standing functions, ideally in separate modules. With respect to the earlier discussion of type wrappers, this can also be implemented with a template parameters that controls the operator-overloading of *, something like below. struct Foo(bool mtimesOverload = false) { template opBinary(string op = "*")(Foo foo) { static if (mtimesOverload) { return mtimes(this, foo); } else { return times(this, foo); } } } So you could do a Matrix alias that has mtimesOverload=true.
Re: gdc is in
On Wednesday, 21 June 2017 at 15:11:39 UTC, Joakim wrote: the gcc tree: https://gcc.gnu.org/ml/gcc/2017-06/msg00111.html Congratulations to Iain and the gdc team. :) I found out because it's on the front page of HN right now, where commenters are asking questions about D. An update, including the latest 2.076 frontend: https://www.phoronix.com/scan.php?page=news_item&px=D-GCC-v3-Patches
Re: Should we add `a * b` for vectors?
On 10/3/17 3:25 PM, Walter Bright wrote: This is specifically designed to prevent nasty surprises. C++ has a big problem with ADL in that overloading is not encapsulated and any #included header can inadvertently add more overloads that may be entirely unrelated. Any .h can crack open any namespace and insert more overloads into it. It's completely unhygienic and uncontrollable. One thing I would like to have control over is how 'this' is passed. A nice thing about UFCS is I can use it to pass-by-value the faux 'this' parameter, whereas I cannot do this with a member function. It makes a difference in some cases, especially with const functions. It also can be cheaper to pass a small struct by value. None of this is possible with operator overloading. A way out could possibly be to alias a non-member function for the operator. -Steve
Re: Should we add `a * b` for vectors?
On 10/3/2017 5:24 PM, Steven Schveighoffer wrote: It also can be cheaper to pass a small struct by value. Should not be a problem if the compiler inlines it.
Re: gdc is in
On Tuesday, 3 October 2017 at 22:00:51 UTC, Joakim wrote: On Wednesday, 21 June 2017 at 15:11:39 UTC, Joakim wrote: the gcc tree: https://gcc.gnu.org/ml/gcc/2017-06/msg00111.html Congratulations to Iain and the gdc team. :) I found out because it's on the front page of HN right now, where commenters are asking questions about D. An update, including the latest 2.076 frontend: https://www.phoronix.com/scan.php?page=news_item&px=D-GCC-v3-Patches Does this include DMD and static if?
Re: gdc is in
On Tuesday, 3 October 2017 at 22:00:51 UTC, Joakim wrote: On Wednesday, 21 June 2017 at 15:11:39 UTC, Joakim wrote: the gcc tree: https://gcc.gnu.org/ml/gcc/2017-06/msg00111.html Congratulations to Iain and the gdc team. :) I found out because it's on the front page of HN right now, where commenters are asking questions about D. An update, including the latest 2.076 frontend: https://www.phoronix.com/scan.php?page=news_item&px=D-GCC-v3-Patches Cool, which version of GCC will have it?