Efficiently passing structs

bitwise via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue May 5 14:21:05 PDT 2015


On Tue, 05 May 2015 11:54:53 -0400, Jonathan M Davis <jmdavisProg at gmx.com>  
wrote:

> On Tuesday, 5 May 2015 at 02:47:03 UTC, bitwise wrote:
>> On Mon, 04 May 2015 00:16:03 -0400, Jonathan M Davis via  
>> Digitalmars-d-learn <digitalmars-d-learn at puremagic.com> wrote:
>>> D will move the argument if it can rather than copying it (e.g. if a
>>> temporary is being passed in), which reduces the need for worrying  
>>> about
>>> copying like you tend to have to do in C++98, and I think that a lot  
>>> of D
>>> code just doesn't worry about the cost of copying structs.
>>
>> How exactly would you move a struct? Just a memcpy without the postblit?
>
> Because D has postblit constructors rather than copy constructors,  
> copying is done by blitting the entire struct and then calling the  
> postlblit constructor afterwards, so unless the postlbit constructor is  
> @disabled, a struct is moveable simply by blitting it and not calling  
> the postblit constructor afterwards. And the compiler can choose to do a  
> move whenever a copy is unnecessary (e.g. return value optimization or  
> when a temporary is passed to a

Gotcha.

Correct me if I'm wrong though, but in C++(and I don't see why D would be  
different), RVO removes the need for the struct/class to be blitted  
completely. The struct/class will simply be constructed directly to the  
return address to begin with.

I don't see how the above could achieved with parameter passing(by value),  
which is why I suggest rvref.


>> Something like a Matrix4x4 lives in an awkward place between a class  
>> and a struct. Because of the fact that a graphics engine may have to  
>> deal with thousands of them per frame, both copying them at function  
>> calls, and allocating/collecting thousands of them per frame, are both  
>> unacceptable.
>>
>> I was reading up(DIP36, pull requests, forum) and it seems like auto  
>> ref was supposed to do something like this. Is there a reason you  
>> didn't mention it?
>
> You could use auto ref, but then you'd have to templatize the function,  
> since it only works with templated functions, and if you have multiple  
> auto ref parameters, then you'll get a combinatorial explosion of  
> template instantations as you call the function with different  
> combinations of lvalues and rvalues. It's basically like declaring each  
> of the combinations of the function with ref and non-ref parameters, but  
> you don't have to declare them all, and it doesn't work with virtual  
> functions. I didn't mention auto ref mostly just to be simple.
>
> But because of that combinatorial explosion (be they declared implicitly  
> via auto ref or manually) is a good reason IMHO to just not worry about  
> this problem in most cases. It's just too tedious to duplicate all  
> functions like that, and using templates isn't always acceptable.
>
> In theory, auto ref could work for non-templated functions by making it  
> so that underneath the hood as ref except that any time you passed it an  
> rvalue, it implicitly defined an lvalue for you to pass to the function,  
> but that doesn't match what happens with auto ref with non-templated  
> functions, and changing the behavior for templated functions would be  
> unacceptable, because it would reduce our ability to forward parameters  
> without changing their type, so we'd end up with auto ref doing  
> different things on templated and non-templated functions, which is  
> potentially confusing. And that solution has simply never been agreed  
> upon. I have no idea if it ever will be.


I'm not really worried about the symbol explosion, because most of the  
functions I would be using would be binary at most. I'm more worried about  
losing the ability to use virtual/non-templated functions with  
lvalue+rvalue refs.

I really do believe this should be fixed. D SHOULD have a way to pass a  
large struct parameter as a ref whether its an lvalue or an rvalue without  
copying. And when I say copying, this includes blitting without calling  
postblit, which would still be quite pricey for something like a  
Matrix4x4(64 bytes for floats) if it had to happen thousands of times per  
frame in real time. And trading these thousands of copies for  
allocations/collections is just as bad.

>> Why not just add "rvref" to D?
>
> Because we have too many attributes already. It's actually kind of  
> astonishing that we're getting return ref, because Andrei was adamant  
> that we not add any more parameter attributes, because we simply have  
> too many already. I think that the only reasons that return ref is  
> making it in is because of how it solves a real need and how simple it  
> is, whereas Andrei is not at all convinced that having anything like 
> C++'s const& in D is needed. And while it might be nice, for the most  
> part, we _are_ able to mostly write code without worrying about it.

I do understand this, but what's more astonishing is that things have  
gotten this far and there isn't as way to pass a struct without making  
unnecessary copies ;)

In a practical sense, I suppose templates/auto ref will mitigate the  
problem _well_enough_ for now, but it's an hack that only works with  
templates and my code will still be creating copies all over the place for  
no reason.

   Bit


More information about the Digitalmars-d-learn mailing list