About ref used for performance reasons with struct

Martin Nowak code at dawg.eu
Tue Feb 12 15:34:16 PST 2013


On 02/11/2013 07:52 AM, deadalnix wrote:
> Ok, We have 2 usages of ref : when you actually need to modify
> informations, and for performance reasons. Let's talk about the second one.
>
> Passing by ref to improve performance is not ideal. First this is quite
> hard to know when it is actually faster to pass by ref and to pass by
> value, especially in generic code. Secondly it is easy to forget to use
> ref at some location, and a lot of small performance improvement are
> lost in the process. Finally, this may be error prone.
>
> I'm thinking about it for a while now and I'm now convinced that we
> should allow the compiler to do that job for us. Let me explain.
>
> When a function accept a struct, the compiler is free to use that
> function, or an altered one taking a reference as parameter. Here are
> some rules the compiler can use to know which one to call from callee
> side :
>
> 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 isn't modified for the vanilla function, but must create a
> local copy of the argument in the following reasons in the ref version :
>   - The argument is binded to a mutable ref.(even as hidden argument as
> for member method).
>   - The argument is actually modified.
>   - address of anything coming from the struct is taken.
>
> The compiler is free to apply such treatment only in a branch of the
> callee, ie :
>
> // Compiler choose to create an alternative ref version for performance.
> void foo(MyStruct s) {
>      if(condition) {
>          // Operation require a copy to not alter what the caller see.
>          // A s is copied on stack and postblit is called.
>          s.field = 5;
>      } else {
>          // No operation requiring local copy is performed.
>          // No local copy is created.
>      }
> }
>
> The compiler is however disallowed to create multiple copies in the
> callee. If several branches requires it, then the copy have to be made
> in a common branch.
>
> Note that the compiler don't HAVE to do this, but is allowed to.
> Modifying the spec in such way allow the compiler to avoid many copies
> of struct let us get rid of most ref parameters, keeping them for what
> they really are for.

You can easily create a function that turns values into rvalues if it's 
opportune.

template isBetterToPassAsValue(T)
{
   enum isBetterToPassAsValue = ...;
}

T cheapPass(T)(ref T t) if (isBetterToPassAsValue!T)
{
   return t;
}

ref T cheapPass(T)(ref T t) if (!isBetterToPassAsValue!T)
{
   return t;
}

T cheapPass(T)(T t) if (isBetterToPassAsValue!T)
{
   return move(t);
}

void foo(T)(auto ref T t)
{
   // ...
}

void bar()
{
   S1 s1;
   foo(cheapPass(s1)); // passed by ref
   S2 s2;
   foo(cheapPass(s2)); // passed by value
}



More information about the Digitalmars-d mailing list