www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - About structs and performant handling

reply "Namespace" <rswhite4 googlemail.com> writes:
I would first like to apologize my bad English.

I like to suggest a possible solution for the current rvalue ref 
problem (especially for structs).
My suggestion is based on the idea of 'auto ref' and the proposal 
of deadalnix which was discussed here: 
http://forum.dlang.org/thread/funxviipfkdftmdfyrfk forum.dlang.org?page=1

Smaller structs are very performant if you move or copy them. 
This is more performant as to create them only to pass them by 
ref.
But structs with a bigger size aren't that performant if you pass 
them as copy or move them (See also my small benchmark: 
http://dpaste.1azy.net/edit/b9624e01).
So I like to suggest a new syntax for this behaviour.
If you want that your function/method/whatever takes a struct as 
well as rvalue and lvalue, you declare this parameter with a '&' 
(Or whatever, the syntax doesn't matter at all. I like the '&' 
because I know it from C++ and many (C++) Newcomer will know what 
it means. Furthermore '&' is a lot shorter than eg. 'auto ref').
For example:
[code]
struct A { }
void foo(A& a) { }
[/code]
The compiler will check by these kind of parameters if they are 
structs and if the size is proven greater as N (maybe 16 - 24) 
bit. If not, the '&' will be ignored. The function take in this 
cases normally lvalues as copy and moves rvalues.
But if the struct size is greater than N the compiler changes the 
storage class of this parameter to ref.
Example:
If you have another struct B with the following structure 
(instead of the lightweight struct A):
[code]
struct B {
public:
	int[100] ids;
}
[/code]
the method 'foo' will be changed to:
[code]
void foo(ref B b) { }
[/code]
In this case lvalues are taken by ref and in case that a rvalue 
is used, a temporary variable is created and  passed to the 
function (like C++ does). Or if you don't like temporary 
variables you could also use a wrapper, something like:
[code]
 property
ref T make(T, Args...)(Args args) {
	static if (args.length != 0) {
		static T result = void;
		T _temp = T(args);
		memcpy(&result, &_temp, T.sizeof);
	} else {
		static T result;
	}
	
	return result;
}
[/code]
So the two possible kind of calls to foo would be:
[code]
B b;
foo(b);
[/code]
and
[code]foo(B());[/code]
which is converted to:
[code]foo(make!B);[/code]

I see a lot of potential in this solution, because the compiler 
is taking care about gaining the most powerfull/performant code, 
it's simple (and simple is always good) AND it could solve the 
rvalue ref problem.
And now you can behead me.
Mar 09 2013
next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
 (See also my small benchmark: 
 http://dpaste.1azy.net/edit/b9624e01).
Wrong link... I meant: http://dpaste.1azy.net/b9624e01
Mar 09 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Namespace:

 Wrong link...
 I meant: http://dpaste.1azy.net/b9624e01
Benchmarks on dpaste aren't very useful because I think no optimization switches are used, and because the CPU is not under control, so other unknown tasks can steal some of its time. Bye, bearophile
Mar 09 2013
parent "Namespace" <rswhite4 googlemail.com> writes:
 Benchmarks on dpaste aren't very useful because I think no 
 optimization switches are used, and because the CPU is not 
 under control, so other unknown tasks can steal some of its 
 time.

 Bye,
 bearophile
I used optimation switches: Application arguments: -O -release -noboundscheck But you're right, but what should I do? I could deliver you my results from my pc: [quote] Call b0 (B by ref). Duration: 259 total, 0.129500 average. Call b1 (B by move). Duration: 804 total, 0.402000 average. Call b2 (B by make). Duration: 364 total, 0.182000 average. Call b3 (B by copy). Duration: 943 total, 0.471500 average. Call b4 (B by manual move). Duration: 1101 total, 0.550500 average. Call b5 (A by move). Duration: 17 total, 0.008500 average. Call b6 (A by copy). Duration: 65 total, 0.032500 average. Call b7 (A by ref). Duration: 47 total, 0.023500 average. Call b8 (A by make). Duration: 54 total, 0.027000 average. [/quote] Also compiled with -O -release -noboundscheck on a Intel i5-2500k CPU with 3.30 GHz. But the script is there. So you could test by yourself. :)
Mar 09 2013
prev sibling next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/09/2013 12:19 PM, Namespace wrote:

 But structs with a bigger size aren't that performant if you pass them
 as copy or move them (See also my small benchmark:
I have started working on the DConf presentation about copy and move semantics in D. I have done exactly the same type of tests and was surprised how faster pass-by-reference can be. To be fair, I have also accessed the members of the structs inside the function to see whether the pointer dereferencing in the by-ref case brought any cost. Apparently I have been ignorant in modern CPU designs because I was surprised to see that pointer dereferencing seemingly had no cost at all. My guess would be that the object is completely inside the processor's cache. Then I suspected dmd and made similar tests with gcc in the C language and have seen similar results. So yes, apparently by-ref is faster at least in some cases.
 For example:
 [code]
 struct A { }
 void foo(A& a) { }
 [/code]
 The compiler will check by these kind of parameters if they are structs
 and if the size is proven greater as N (maybe 16 - 24) bit. If not, the
 '&' will be ignored. The function take in this cases normally lvalues as
 copy and moves rvalues.
 But if the struct size is greater than N the compiler changes the
 storage class of this parameter to ref.
I hope others with compiler knowledge will chime in here. I think the type of the parameter that is passed is intrinsic to how the function gets compiled. I think, for that to work, the compiler would have to compile two versions of the function, one taking by-value and the other taking by-ref. If what I said above is correct, then of course that wouldn't scale, e.g. we would need four separate compilations of the function if we had two parameters. Then there would be the issue of finding a naming scheme for these separate versions of the function so that the linker finds the right one. I am making up some names for the linker: foo_val_val(), foo_val_ref(), foo_ref_val(), foo_ref_ref(). Others, please correct me if I am wrong above. :) Ali
Mar 09 2013
next sibling parent Marco Leise <Marco.Leise gmx.de> writes:
Am Sat, 09 Mar 2013 15:07:49 -0800
schrieb Ali =C3=87ehreli <acehreli yahoo.com>:

 Apparently I have been ignorant in modern CPU designs=20
 because I was surprised to see that pointer dereferencing seemingly had=20
 no cost at all. My guess would be that the object is completely inside=20
 the processor's cache.
Be aware of several things playing together here: L1 and L2 cache as well as prefetching and order of the data in memory. If you create a few KiB of data and run it through a test its all in the L1 cache and blazing fast. If you have a game and load a matrix struct from somewhere scattered in memory you'll see the massive access penalty. The modern prefetchers in CPUs keep track of a N streams of forward or backward serial memory accesses. So they work perfectly for iterating an array for example. The work in the "background" and use free memory bandwidth to load data from RAM to CPU caches before you actually need it. This hides the memory delay that has become increasingly larger in the past years. It is so important that many don't optimize for CPU cycles anymore but instead for memory access and cache locality: * http://en.wikipedia.org/wiki/Judy_array * http://research.scee.net/files/presentations/gcapaustralia09/Pitfalls_of_= Object_Oriented_Programming_GCAP_09.pdf Its easy to underestimate the effects until you benchmark with some several MiB large random memory access patterns and see how you get close to a 100 times slow down. --=20 Marco
Mar 09 2013
prev sibling next sibling parent reply "Daniel Murphy" <yebblies nospamgmail.com> writes:
"Ali Çehreli" <acehreli yahoo.com> wrote in message 
news:khgfc6$1m9i$1 digitalmars.com...
 To be fair, I have also accessed the members of the structs inside the 
 function to see whether the pointer dereferencing in the by-ref case 
 brought any cost. Apparently I have been ignorant in modern CPU designs 
 because I was surprised to see that pointer dereferencing seemingly had no 
 cost at all. My guess would be that the object is completely inside the 
 processor's cache.
Accessing a member of a stuct on the stack: mov EDX, dword ptr [ESP+stackoffset+memberoffset] Accessing a member of a struct on the heap: (assume pointer to struct is in EAX) mov EDX, dword ptr [EAX+memberoffset] A lot of the time the heap pointer will be in a register already. Stack memory will almost always be in the caches, and so will recently used heap memory. If you want to measure the cost of loading the heap pointer then dereferencing, you might want to mark all the registers as used so the compiler is forced to reload. eg with asm {}
 For example:
 [code]
 struct A { }
 void foo(A& a) { }
 [/code]
 The compiler will check by these kind of parameters if they are structs
 and if the size is proven greater as N (maybe 16 - 24) bit. If not, the
 '&' will be ignored. The function take in this cases normally lvalues as
 copy and moves rvalues.
 But if the struct size is greater than N the compiler changes the
 storage class of this parameter to ref.
I hope others with compiler knowledge will chime in here. I think the type of the parameter that is passed is intrinsic to how the function gets compiled. I think, for that to work, the compiler would have to compile two versions of the function, one taking by-value and the other taking by-ref.
A better way to do with would be to change (or extend) the abi, so that structs over a certain size are always passed by reference with this parameter type. Then you only need one version of the function. We could use auto ref for this.
Mar 09 2013
next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
 A better way to do with would be to change (or extend) the abi, 
 so that
 structs over a certain size are always passed by reference with 
 this
 parameter type.
And what if you want to pass it by value? I am a opponent of such automatic and uncontrollable compiler intervention. Better to manually identify something to be on the safe side.
 Then you only need one version of the function.  We could
 use auto ref for this.
'auto ref' will probably never work for non-template functions, as Jonathan said some time ago.
Mar 09 2013
parent reply "Daniel Murphy" <yebblies nospamgmail.com> writes:
"Namespace" <rswhite4 googlemail.com> wrote in message 
news:qmznbrplexqrdqahgaus forum.dlang.org...
 A better way to do with would be to change (or extend) the abi, so that
 structs over a certain size are always passed by reference with this
 parameter type.
And what if you want to pass it by value?
You don't mark it with '&'.
 I am a opponent of such automatic and uncontrollable compiler 
 intervention. Better to manually identify something to be on the safe 
 side.
Then I don't understand the point. If it doesn't need to be automatic, you can just do it in your own code (use ref/not ref)
 Then you only need one version of the function.  We could
 use auto ref for this.
'auto ref' will probably never work for non-template functions, as Jonathan said some time ago.
That is not my understanding.
Mar 09 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
 You don't mark it with '&'.
Oh, then I understood you wrong. I thought you meant that the compiler for each struct parameters (whether marked or not) just automatically perform such changes.
 Then you only need one version of the function.  We could
 use auto ref for this.
'auto ref' will probably never work for non-template functions, as Jonathan said some time ago.
That is not my understanding.
Read my thread 'auto ref - again'.
Mar 09 2013
parent reply "Daniel Murphy" <yebblies nospamgmail.com> writes:
"Namespace" <rswhite4 googlemail.com> wrote in message 
news:aucfrmomyneucfykpmap forum.dlang.org...
 Then you only need one version of the function.  We could
 use auto ref for this.
'auto ref' will probably never work for non-template functions, as Jonathan said some time ago.
That is not my understanding.
Read my thread 'auto ref - again'.
If Jonathan made this claim in there, I must have missed it. Regardless, there is an open pull request that implements auto ref, which has not been accepted or rejected yet.
Mar 09 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
 If Jonathan made this claim in there, I must have missed it.  
 Regardless,
 there is an open pull request that implements auto ref, which 
 has not been
 accepted or rejected yet.
Like I said, read the thread. Or better, read all my threads on this topic.
Mar 10 2013
parent reply "Daniel Murphy" <yebblies nospamgmail.com> writes:
"Namespace" <rswhite4 googlemail.com> wrote in message 
news:hlsfrzwwmwtepuidxgrt forum.dlang.org...
 If Jonathan made this claim in there, I must have missed it.  Regardless,
 there is an open pull request that implements auto ref, which has not 
 been
 accepted or rejected yet.
Like I said, read the thread. Or better, read all my threads on this topic.
I have better things to do.
Mar 10 2013
parent "Namespace" <rswhite4 googlemail.com> writes:
Then just believe me. I have discussed the theme of 'auto ref' a 
lot of times.
And I would be still happy if 'auto ref' would also work for 
non-template functions. But I just think my idea still goes a 
step further. After all, it takes into account the problem of 
moving large structs.
Mar 10 2013
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Sunday, 10 March 2013 at 02:17:36 UTC, Daniel Murphy wrote:
 A better way to do with would be to change (or extend) the abi, 
 so that
 structs over a certain size are always passed by reference with 
 this
 parameter type.  Then you only need one version of the 
 function.  We could
 use auto ref for this.
Auto ref will pass any lvalue by ref, even when it don't make any sense (small structs). It also expose different semantic according if an revalue or an lvalue is passed. It has roughly the same issues as this thread's proposal.
Mar 10 2013
parent "Daniel Murphy" <yebblies nospamgmail.com> writes:
"deadalnix" <deadalnix gmail.com> wrote in message 
news:libldszmvkzcezcfkktx forum.dlang.org...
 On Sunday, 10 March 2013 at 02:17:36 UTC, Daniel Murphy wrote:
 A better way to do with would be to change (or extend) the abi, so that
 structs over a certain size are always passed by reference with this
 parameter type.  Then you only need one version of the function.  We 
 could
 use auto ref for this.
Auto ref will pass any lvalue by ref, even when it don't make any sense (small structs). It also expose different semantic according if an revalue or an lvalue is passed. It has roughly the same issues as this thread's proposal.
You're right, I was thinking about const auto ref. const auto ref could pass values however it felt, and nobody would ever know.
Mar 11 2013
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
 I think the type of the parameter that is passed is intrinsic 
 to how the function gets compiled. I think, for that to work, 
 the compiler would have to compile two versions of the 
 function, one taking by-value and the other taking by-ref.
If we have this we have still the problem that moving a big struct is slow. My 'make' is a lot faster. Therefore I suggested this new behaviour instead of the old deliberations about 'auto ref'. And I thought, because it is probable, that 'auto ref' will never work for non-template function, my Idea would be a nice alternative. :)
Mar 09 2013
prev sibling next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Saturday, 9 March 2013 at 23:07:50 UTC, Ali Çehreli wrote:
 On 03/09/2013 12:19 PM, Namespace wrote:

 But structs with a bigger size aren't that performant if you
pass them
 as copy or move them (See also my small benchmark:
I have started working on the DConf presentation about copy and move semantics in D. I have done exactly the same type of tests and was surprised how faster pass-by-reference can be. To be fair, I have also accessed the members of the structs inside the function to see whether the pointer dereferencing in the by-ref case brought any cost. Apparently I have been ignorant in modern CPU designs because I was surprised to see that pointer dereferencing seemingly had no cost at all. My guess would be that the object is completely inside the processor's cache.
Have you checked the assembly to see if a dereference actually happened ? This should be performant anyway, because the stack frame is likely to be in cache.
 Then I suspected dmd and made similar tests with gcc in the C 
 language and have seen similar results. So yes, apparently 
 by-ref is faster at least in some cases.
Indeed, if the struct is big enough or the copy expensive !
 The compiler will check by these kind of parameters if they
are structs
 and if the size is proven greater as N (maybe 16 - 24) bit.
If not, the
 '&' will be ignored. The function take in this cases normally
lvalues as
 copy and moves rvalues.
 But if the struct size is greater than N the compiler changes
the
 storage class of this parameter to ref.
I hope others with compiler knowledge will chime in here. I think the type of the parameter that is passed is intrinsic to how the function gets compiled. I think, for that to work, the compiler would have to compile two versions of the function, one taking by-value and the other taking by-ref. If what I said above is correct, then of course that wouldn't scale, e.g. we would need four separate compilations of the function if we had two parameters.
I don't think so. Many function's parameters aren't electible to such mecanism (or it'd be a bug) and many type don't gain to be passed by ref (small struct, like slice, delegate, a very large amount in fact).
 Then there would be the issue of finding a naming scheme for 
 these separate versions of the function so that the linker 
 finds the right one. I am making up some names for the linker: 
 foo_val_val(), foo_val_ref(), foo_ref_val(), foo_ref_ref().
Yes, mangling must be affected, but I don't think this is that bad.
Mar 10 2013
prev sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Saturday, 9 March 2013 at 23:07:50 UTC, Ali Çehreli wrote:
 On 03/09/2013 12:19 PM, Namespace wrote:

 But structs with a bigger size aren't that performant if you
pass them
 as copy or move them (See also my small benchmark:
I have started working on the DConf presentation about copy and move semantics in D. I have done exactly the same type of tests and was surprised how faster pass-by-reference can be.
Hey, could you please publish your slides of your DConf presentation? :)
May 08 2013
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 05/08/2013 05:57 AM, Namespace wrote:

 On Saturday, 9 March 2013 at 23:07:50 UTC, Ali Çehreli wrote:
 On 03/09/2013 12:19 PM, Namespace wrote:

 But structs with a bigger size aren't that performant if you
pass them
 as copy or move them (See also my small benchmark:
I have started working on the DConf presentation about copy and move semantics in D. I have done exactly the same type of tests and was surprised how faster pass-by-reference can be.
Hey, could you please publish your slides of your DConf presentation? :)
Yes, they are here: http://acehreli.org/AliCehreli_copy_move_D.pdf But I see that you have been after some test results between by-copy vs. by-ref parameters. I have decided not to include them in the presentation for various reasons: not enough presentation time, not trusting my synthetic tests enough to be sure that I was covering all aspects of the behaviors of modern cpu architectures, etc. I can say that by-value was almost always slower in my little test. However, as others have been saying, by-ref can be slower if a lot of the members of the struct are accessed through that reference, effectively defeating the CPU caches. For me to see that by-ref was slower, I had to define huge structs, put them in huge arrays, and touch all of the members of random elements of that array: // s1 and s2 are random elements of huge arrays void foo(ref const(S) s1, ref const(S) s2) { // use all members of s1 and s2 } Only then by-ref was slower than by-value. Let me build more trust in my test before opening it to discussion on the forums. Ali
May 08 2013
parent "Namespace" <rswhite4 googlemail.com> writes:
I was wondering where I can find your benchmarks as I read your 
slides. Now it has become clearer. I'm looking forward to reading 
your benchmarks and assessments. :)
May 08 2013
prev sibling next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
Too bad, I had hoped for a little more discussion / feedback on 
this topic.
Is it a good idea or a bad one. It is complete nonsense or has it 
been a right to exist, etc.
What's wrong with you guys?
Mar 10 2013
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/10/13 2:57 PM, Namespace wrote:
 Too bad, I had hoped for a little more discussion / feedback on this topic.
 Is it a good idea or a bad one. It is complete nonsense or has it been a
 right to exist, etc.
 What's wrong with you guys?
I can't hear or read "what's wrong with you" without remembering about https://www.youtube.com/watch?v=nV7u1VBhWCE... Andrei
Mar 10 2013
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Saturday, 9 March 2013 at 20:19:10 UTC, Namespace wrote:
 So I like to suggest a new syntax for this behaviour.
 If you want that your function/method/whatever takes a struct 
 as well as rvalue and lvalue, you declare this parameter with a 
 '&' (Or whatever, the syntax doesn't matter at all. I like the 
 '&' because I know it from C++ and many (C++) Newcomer will 
 know what it means. Furthermore '&' is a lot shorter than eg. 
 'auto ref').
I don't think this is a good idea for several reasons. First, it introduce a new syntax, which is always a concern. It add language complexity, and it likely to not be used everywhere it should. Second, the default behavior must be the most beneficial one. Simply because it will be the most used, and we don't want good code to be crippled with many addition here and there. Providing a default suboptimal behavior (here performancewise). Finally, I think this behavior is very dangerous. Pass by reference and value have very different. With that construct, you'll never know which one you get as the compiler decide ! If the compiler and not the programmer decide what the program does (not implementation wise, but semantically), we have a problem.
Mar 10 2013
next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
 Finally, I think this behavior is very dangerous. Pass by 
 reference and value have very different. With that construct, 
 you'll never know which one you get as the compiler decide ! If 
 the compiler and not the programmer decide what the program 
 does (not implementation wise, but semantically), we have a 
 problem.
Haven't you then not the same problems with 'auto ref'? But you could prohibit any manipulation of a '&' parameter. Eg. you could make him 'scope' by default (if 'scope' will work someday).
Mar 10 2013
next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Sunday, 10 March 2013 at 19:27:01 UTC, Namespace wrote:
 Finally, I think this behavior is very dangerous. Pass by 
 reference and value have very different. With that construct, 
 you'll never know which one you get as the compiler decide ! 
 If the compiler and not the programmer decide what the program 
 does (not implementation wise, but semantically), we have a 
 problem.
Haven't you then not the same problems with 'auto ref'? But you could prohibit any manipulation of a '&' parameter. Eg. you could make him 'scope' by default (if 'scope' will work someday).
As said in another post, I think auto ref suffer pretty much the same problem, but it is less problematic as you can't see the result on rvalues anyway, so the compiler cannot really screw you. It has the inconvenience of taking by ref many thing that would benefit from pass by value.
Mar 10 2013
parent "Namespace" <rswhite4 googlemail.com> writes:
 As said in another post, I think auto ref suffer pretty much 
 the same problem, but it is less problematic as you can't see 
 the result on rvalues anyway, so the compiler cannot really 
 screw you. It has the inconvenience of taking by ref many thing 
 that would benefit from pass by value.
The only problem with this is, that no one knows, if and when 'auto ref' will be implemented for non-templates. I still hope to an official statement, but I think this is unfortunately unrealistic.
Mar 11 2013
prev sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Sunday, 10 March 2013 at 19:27:01 UTC, Namespace wrote:
 But you could prohibit any manipulation of a '&' parameter. Eg. 
 you could make him 'scope' by default (if 'scope' will work 
 someday).
A better solution would be to require that a Parameter with '&' need 'const scope' as storage class. So the syntax would be: void foo(in A& a) { In my opinion, there is then no reason to worry about whether it is an lvalue or an rvalue. It cannot be changed or referenced. This: void foo(A& a) { without const scope would cause an error like : "Error: '&' requires const scope." AFAIK the syntax 'in ref' was suggested by Kenji but was rejected for some reasons.
Mar 11 2013
parent reply "Zach the Mystic" <reachzach gggggmail.com> writes:
On Monday, 11 March 2013 at 10:55:50 UTC, Namespace wrote:
 On Sunday, 10 March 2013 at 19:27:01 UTC, Namespace wrote:
 But you could prohibit any manipulation of a '&' parameter. 
 Eg. you could make him 'scope' by default (if 'scope' will 
 work someday).
A better solution would be to require that a Parameter with '&' need 'const scope' as storage class. So the syntax would be: void foo(in A& a) { In my opinion, there is then no reason to worry about whether it is an lvalue or an rvalue. It cannot be changed or referenced. This: void foo(A& a) { without const scope would cause an error like : "Error: '&' requires const scope." AFAIK the syntax 'in ref' was suggested by Kenji but was rejected for some reasons.
Your point reminds me of the fact that D has no "inline" or "register" keywords. You probably couldn't pick better experts than the D compiler builders for deciding whether or not those two keywords are necessary. I guess there's not a clear solution to this question, or it would have been settled a while ago. Maybe it should be called the "performance ref" issue, to distinguish it from other ref issues, " safe ref", "auto ref", etc.
Mar 11 2013
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 12 March 2013 at 06:36:32 UTC, Zach the Mystic wrote:
 Your point reminds me of the fact that D has no "inline" or 
 "register" keywords. You probably couldn't pick better experts 
 than the D compiler builders for deciding whether or not those 
 two keywords are necessary. I guess there's not a clear 
 solution to this question, or it would have been settled a 
 while ago. Maybe it should be called the "performance ref" 
 issue, to distinguish it from other ref issues, " safe ref", 
 "auto ref", etc.
Register keyword make no sense nowadays. Compiler can figure it out way better than you. You could forget about some register keyword and slow down you program for nothing. You could use some register keyword and create bug because you got your aliasing wrong. And all that would require a huge amount of work and clutter the codebase. register keyword is a relic from the past, even in C. inlining is more subtle. In the general case, compiler get it better than humans, however, it is a much harder problem than register, so being able to hint the compiler may be useful. However, the usage of this is limited to very specific use cases. I don't see any reason to not allow the compiler to do so and create copy on a per needed basis, by optimizing pass-by value into pass-by-reference when it can prove it doesn't change the semantic (see my thread on the topic, but modify it to apply the restriction to anything possibly aliased). For the same reason as inline and register, I do think the default behavior is to let the compiler choose what is the bast as long as it doesn't change the final behavior.
Mar 12 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
I also think that we do not need 'inline' or 'register'. 
Nowadays, compilers can really assess the situation much better 
than we do.

 I don't see any reason to not allow the compiler to do so and 
 create copy on a per needed basis, by optimizing pass-by value 
 into pass-by-reference when it can prove it doesn't change the 
 semantic (see my thread on the topic, but modify it to apply 
 the restriction to anything possibly aliased).

 For the same reason as inline and register, I do think the 
 default behavior is to let the compiler choose what is the bast 
 as long as it doesn't change the final behavior.
I know your thread, I was one of the most active there. And I liked the idea, provided such parameters must be still manually denoted. And if such parameters are also denoted with 'in' or 'const scope', I see no way that they could be manipulated. I was hoping that Andrei and Walter could say something about the topic. I like to see something like that and it would solve the 'rvalue ref' problem also.
Mar 12 2013
next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 12 March 2013 at 08:21:50 UTC, Namespace wrote:
 I also think that we do not need 'inline' or 'register'. 
 Nowadays, compilers can really assess the situation much better 
 than we do.

 I don't see any reason to not allow the compiler to do so and 
 create copy on a per needed basis, by optimizing pass-by value 
 into pass-by-reference when it can prove it doesn't change the 
 semantic (see my thread on the topic, but modify it to apply 
 the restriction to anything possibly aliased).

 For the same reason as inline and register, I do think the 
 default behavior is to let the compiler choose what is the 
 bast as long as it doesn't change the final behavior.
I know your thread, I was one of the most active there. And I liked the idea, provided such parameters must be still manually denoted.
You never gave any rationale reason on that.
Mar 12 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
 You never gave any rationale reason on that.
Because I just like to have the control over what is passed by ref and what by value. Maybe I want that a bigger struct is passed by value. As long as I must denote parameters accordingly, in order that the compiler decide instead of me, this is no problem. But if you have no control, because the compiler will decide for you, it would not be possible. I'm considering a small example until evening.
Mar 12 2013
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 12 March 2013 at 09:24:11 UTC, Namespace wrote:
 You never gave any rationale reason on that.
Because I just like to have the control over what is passed by ref and what by value.
I'm sorry, but what you like is exactly the opposite of a rational example.
 Maybe I want that a bigger struct is passed by value.
 As long as I must denote parameters accordingly, in order that 
 the compiler decide instead of me, this is no problem.
 But if you have no control, because the compiler will decide 
 for you, it would not be possible.
 I'm considering a small example until evening.
Mar 12 2013
next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Tuesday, 12 March 2013 at 09:38:19 UTC, deadalnix wrote:
 On Tuesday, 12 March 2013 at 09:24:11 UTC, Namespace wrote:
 You never gave any rationale reason on that.
Because I just like to have the control over what is passed by ref and what by value.
I'm sorry, but what you like is exactly the opposite of a rational example.
I'll always like to disabuse. So why do you think this is not a rational example? And what would a rational example for you?
Mar 12 2013
parent "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 12 March 2013 at 11:24:43 UTC, Namespace wrote:
 On Tuesday, 12 March 2013 at 09:38:19 UTC, deadalnix wrote:
 On Tuesday, 12 March 2013 at 09:24:11 UTC, Namespace wrote:
 You never gave any rationale reason on that.
Because I just like to have the control over what is passed by ref and what by value.
I'm sorry, but what you like is exactly the opposite of a rational example.
I'll always like to disabuse. So why do you think this is not a rational example? And what would a rational example for you?
Let's consider the given example : struct Foo { uint i; this(this) { writeln("foo"); } } void bar(Foo f) { writeln("bar"); } uint main() { Foo f; f.i = 42; bar(f); return f.i, } You think you pass by value ? In fact, if you let the optimizer do its job, you don't even have a function call. Thing gets rewriten this way : uint main() { writeln("foo"); writeln("bar"); return 42; } You have no function call and no pass by value anymore. In fact, the only thing the current definition garantee it that you have the side effect of the postblit (ie, memory allocation, etc . . .), but nothing is said about having an actual function call or a pass by value. If you think you control the code that way, you are living a delusional world, because the compiler rewrite everything under you. You are asking to keep that illusion in place by asking for an explicit syntax to allow the optimization to take place. And note that this is very good thing as the code generated is much faster and the code much cleaner. If you had to specify optimisations, you'd get them wrong, you'd forget them, your code would be less reliable, slower and less readable.
Mar 12 2013
prev sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Tuesday, 12 March 2013 at 09:38:19 UTC, deadalnix wrote:
 On Tuesday, 12 March 2013 at 09:24:11 UTC, Namespace wrote:
 You never gave any rationale reason on that.
Because I just like to have the control over what is passed by ref and what by value.
I'm sorry, but what you like is exactly the opposite of a rational example.
I've misunderstood you, ignore my post above please. Well, I said "I like [...]" but I meant "It's important to controll what is passed by ref and what by value.". But I haven't yet an example for this. So maybe you're right. Whatever, our first concern should be to stimulate thinking of this and to call attention of Walter and/or Andrei. Then we can still debated whether such parameters should be labeled as such, or not.
Mar 12 2013
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 12 March 2013 at 11:58:26 UTC, Namespace wrote:
 On Tuesday, 12 March 2013 at 09:38:19 UTC, deadalnix wrote:
 On Tuesday, 12 March 2013 at 09:24:11 UTC, Namespace wrote:
 You never gave any rationale reason on that.
Because I just like to have the control over what is passed by ref and what by value.
I'm sorry, but what you like is exactly the opposite of a rational example.
I've misunderstood you, ignore my post above please. Well, I said "I like [...]" but I meant "It's important to controll what is passed by ref and what by value.". But I haven't yet an example for this. So maybe you're right. Whatever, our first concern should be to stimulate thinking of this and to call attention of Walter and/or Andrei. Then we can still debated whether such parameters should be labeled as such, or not.
Here is what I think. How the thing is actually passed don't matter. What does matter is that the semantic is know : ie that the program will behave in a known way. As a consequence, if the compiler choose to pass by ref instead of passing by value as an optimization, it must do so only if it can prove that the resulting code will do the same thing.
Mar 12 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
 Here is what I think. How the thing is actually passed don't 
 matter. What does matter is that the semantic is know : ie that 
 the program will behave in a known way.
I agree.
 As a consequence, if the compiler choose to pass by ref instead 
 of passing by value as an optimization, it must do so only if 
 it can prove that the resulting code will do the same thing.
And how could it be proved? IMO with const (not mutable) scope (no escaping). What are your thoughts?
Mar 12 2013
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 12 March 2013 at 15:19:14 UTC, Namespace wrote:
 As a consequence, if the compiler choose to pass by ref 
 instead of passing by value as an optimization, it must do so 
 only if it can prove that the resulting code will do the same 
 thing.
And how could it be proved? IMO with const (not mutable) scope (no escaping). What are your thoughts?
To repeat myself : The caller is free to call the ref version of the function unless (rules evaluate in order) : - The argument is an rvalue (in such case, no postblit is executed as well). - The argument is shared. - The argument's postblit in not pure (weakly). The callee must create a local copy if (this is not verbatim from previous post as it has limitations as been pointed pointed) : - Mutate the struct or anything that *may* alias/be transitively reached through the struct. - Pass by ref/address taken to a non const/non weakly pure function (including method call) of the struct or anything that *may* alias/be transitively reached through the struct. It does guarantee that visible result will be the same.
Mar 12 2013
next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
 To repeat myself :

 The caller is free to call the ref version of the function 
 unless
 (rules evaluate in order) :
  - The argument is an rvalue (in such case, no postblit is
 executed as well).
  - The argument is shared.
  - The argument's postblit in not pure (weakly).

 The callee must create a local copy if (this is not verbatim 
 from previous post as it has limitations as been pointed 
 pointed) :
  - Mutate the struct or anything that *may* alias/be 
 transitively reached through the struct.
  - Pass by ref/address taken to a non const/non weakly pure 
 function (including method call) of the struct or anything that 
 *may* alias/be transitively reached through the struct.

 It does guarantee that visible result will be the same.
Because I do not understand everything (due to my bad English) I would be glad to see one or maybe more examples where the compiler (according to your rules) change pass by value into pass by reference, and where not. And the problem remains that we have to get the attention of Walter or Andrei.
Mar 12 2013
prev sibling parent "Namespace" <rswhite4 googlemail.com> writes:
One option, that I could think of why one should mark such 
parameters, would be that the compiler can then point out what is 
missing so that it would be potentially possible.

Example:
void foo (A a) {

Because 'a' is not const and not scope, the compiler could not 
change the behavior from passing by value to passing by reference.

But when the parameter is marked accordingly:
void foo (A& a) {

You get an error / warning:
"Parameters with '&' require const scope."
Whether 'A' ultimately is really big enough to make it worthwhile 
to have it passed by ref is irrelevant for that.
Mar 12 2013
prev sibling parent Marco Leise <Marco.Leise gmx.de> writes:
Am Tue, 12 Mar 2013 09:21:49 +0100
schrieb "Namespace" <rswhite4 googlemail.com>:

 I also think that we do not need 'inline' or 'register'. 
 Nowadays, compilers can really assess the situation much better 
 than we do.
Not to forget that ever since we have Intel, AMD and different generations of complex CPUs (branch prediction, prefetching, ...) in coexistence all the work you do to optimize on your CPU (which improves over the compiler generated code) might slow down the next CPU. Maybe embedded developers disagree with my x86 centristic view, though :) -- Marco
Mar 12 2013
prev sibling parent "Namespace" <rswhite4 googlemail.com> writes:
 First, it introduce a new syntax, which is always a concern. It 
 add language complexity, and it likely to not be used 
 everywhere it should.
Yes, you're right. But I also said that the syntax does not matter. My intention was to stimulate thinking about the real issue. Even if "auto ref" will work someday for non-templates (we really need an official statement about that), then even small structs will be passed by ref. And that's still very ugly.
Mar 10 2013