Kinds of containers
Brad Anderson via Digitalmars-d
digitalmars-d at puremagic.com
Wed Oct 21 11:48:13 PDT 2015
On Wednesday, 21 October 2015 at 18:05:07 UTC, Jonathan M Davis
wrote:
> On Wednesday, 21 October 2015 at 16:36:46 UTC, Brad Anderson
> wrote:
>> On Wednesday, 21 October 2015 at 11:05:12 UTC, Andrei
>> Alexandrescu wrote:
>> [snip]
>>> 2. Reference containers.
>>>
>>> These have classic reference semantics (à la Java).
>>> Internally, they may be implemented either as class objects
>>> or as reference counted structs.
>>>
>>> They're by default mutable. Qualifiers should apply to them
>>> gracefully.
>>>
>>> 3. Eager value containers.
>>>
>>> These are STL-style. Somewhat surprisingly I think these are
>>> the worst of the pack; they expensively duplicate at the drop
>>> of a hat and need to be carefully passed around by reference
>>> lest performance silently drops. Nevertheless, when used as
>>> members inside other data structures value semantics might be
>>> the appropriate choice. Also, thinking of them as values
>>> often makes code simpler.
>>>
>>> By default eager value containers are mutable. They should
>>> support immutable and const meaningfully.
>>
>> Having both reference and value semantics for containers would
>> be great. I don't understand why reference semantics would be
>> implemented by the container themselves though. Why not a
>> general purpose RC! (or RefCounted! if the current design is
>> deemed sufficient) that can apply to anything, including
>> containers? Then you'd only need to implement the value
>> semantic containers (and maybe throw in some RC version
>> aliases to promote the use of the RC versions so the option
>> isn't overlooked). It seems kind of crazy that anything in D
>> that wants to be reference counted would need to implement the
>> logic themselves.
>>
>> If there are performance advantages (I haven't thought of any
>> but perhaps there are) to bake the RC right into the container
>> it might also be possible to use DbI take advantage of it in
>> RC! when appropriate.
>>
>> It just seems so wrong to implement reference counting dozens
>> of times independently, especially when that means
>> implementing all the containers twice too.
>
> If we had value type containers and reference type containers,
> I would assume that they would at least share implementation,
> and maybe the reference types would just be wrappers around the
> value types. However, I completely fail to understand why you'd
> ever want a container that was a value type.
Well they are more efficient when used correctly (no inc/decs).
Using them correctly does take some additional knowledge and
consideration though. C++11 did take care of a whole class of
implicit copying and RVO has long taken care of another class of
them. I don't think it happens (in C++) quite as often as you are
making it out to be (at least in my experience) but I'll admit I
have had a bug or two caused by modifying a copy when I thought I
was changing a reference. I could just as easily have bugs caused
by modifying a reference when I thought I was modifying a copy
though. Either approach comes with its own considerations.
> In my experience, it's very error-prone and adds no value.
Unless we end up with compiler assisted inc/dec elision they are
always going to be faster. Perhaps marginally faster but still
faster and the realtime guys are always going to demand the
fastest option.
> It just makes it too easy to accidentally copy a container, and
> it can be pretty easy to have an iterator, range, etc.
> referring to a container that's already been destroyed (similar
> to having a dynamic array referring to a static array that's
> left scope).
Having the ranges of a container keep the reference count would
make the speed advantage of the value types even more of a
factor. That might actually change the performance advantage away
from being just subtle to being strong. We'd need to benchmark to
say for sure, of course.
Just a sidenote regarding iterator invalidation: C++, when
following the new C++ Core Guidelines, is actually able to do
static analysis of iterators for memory safety without ref count
overhead. Herb Sutter showed a preview version of MSVC doing it
during his keynote this month at CppCon[1]. It blew me away that
they were about to do that with C++.
> As long as the containers have a dup method (or whatever we
> call it) so that they can be copied when you do want to copy
> them, I would think that that was more than enough. What do you
> get with a value type container that you consider better than a
> reference type? It's not like it lives on the stack as a value
> type. Most of the container's guts are on the heap regardless.
>
> - Jonathan M Davis
All that said, I agree with you that the implicit copying isn't
really all that desirable. Perhaps there is a better way. What if
instead of value semantics we had unique containers (akin to
std.typecons.Unique or unique_ptr in C++) and required people to
always state whether it was a copy or move? That takes care of
the error prone implicit copying while retaining the performance
characteristics.
1. https://www.youtube.com/watch?v=hEx5DNLWGgA
More information about the Digitalmars-d
mailing list