Kinds of containers
Jonathan M Davis via Digitalmars-d
digitalmars-d at puremagic.com
Wed Oct 21 14:02:26 PDT 2015
On Wednesday, 21 October 2015 at 20:13:31 UTC, jmh530 wrote:
> On Wednesday, 21 October 2015 at 18:38:10 UTC, Jonathan M Davis
> wrote:
>>
>> A static array is of a fixed size, which almost no other
>> containers are. It also lives entirely on the stack, which
>> almost no other containers do. If there's a container that
>> lives entirely on the stack, then maybe it would make sense
>> for it to be a value type, but _very_ few containers fall in
>> that category, and all of the classic containers like vector,
>> linked list, map, etc. have no business being value types
>> IMHO. It's just error-prone. Heck, static arrays are quite
>> error-prone thanks to the fact that they convert to dynamic
>> arrays, but they do serve a purpose. So, maybe there are
>> containers that fall in the same category, but I expect that
>> such containers are pretty obviously value types and not
>> reference types, because their nature makes them that way.
>> Regardless, I don't see how it's reasonable in general to make
>> a container be a value type. It's just asking for trouble. If
>> there's any question at all whether a container should be a
>> value type or a reference type, IMHO, it should be a reference
>> type.
>>
>
> I do find myself mixing up static and dynamic arrays...
>
> I'll generally defer to you. I suppose within the context of
> Andrei working on containers with std.allocator, then
> containers that only live on the stack seems beyond the scope
> anyway.
>
> Your statement just had struck me as weird as I had thought of
> people like manu saying at various points that they only kept
> data structures on the stack. My next thought was that static
> arrays are usually on the stack and are value types. So I
> figured that whatever the performance people are using were
> similar.
Static arrays are a bit of a special case, because they're
essentially a chunk of memory on the stack. They can be extremely
useful and efficient, but you have to be careful with them. Most
containers don't act anything like them.
But like I said, there are some container types which inherently
make sense on a stack, but most don't. For the most part, the
kinds of containers that we're talking about here - vectors/array
lists, linked lists, maps, sets, etc. - have to have most of
their guts living on the heap. You'd have to _really_ contort
things to put them entirely on the stack, and you'd be seriously
limiting how many elements that they could hold if you did. If
you make a vector or a linked list a value type, then it'll have
a few members on the stack, but most of them will be on the heap,
and it'll be a value type only because its postblit constructor
explicitly copies the portion that's on the heap. It's not really
naturally a value type. It's naturally more of a reference type
or pseudo-reference type.
If the vector is implemented as a reference type, then the few
member variables that were on the stack in the value type version
end up on the heap with the rest of the container's guts, so you
use slightly more memory for them, but it's much more natural
that way, since the main guts of the container were on the heap
anyway. And if the container is a value type, you risk copying
unnecessarily and/or keeping around
references/pointers/iterators/ranges to its elements after it's
been destroyed, whereas with a reference type, it'll only be
copied when you specifically request it. Treating them as value
types is inefficient, error-prone, and unnatural.
Now, folks like Manu do do some crazy stuff eke out every bit of
performance that they can, and they do sometimes use stuff like
alloca to put stuff that would otherwise be on the heap on the
stack. But a lot of what they do isn't very general purpose
either, and they generally avoid stuff like the containers in
standard libraries, because they use the heap too much and/or
aren't tailored to their needs. They also tend to do stuff that's
way more error-prone in an effort to eke out that extra bit of
performance.
If we can do stuff to support them, then great, but in general -
especially when you're talking about the library and not the
language - the kinds of stuff that they want don't work for other
folks. If someone like Manu wants an efficient container, he
_might_ want one that's not a full-on reference type simply so
that less of it is on the heap, but he sure isn't going to want
to copy it except when he absolutely has to. So, the fact that
it's a value type isn't necessarily useful. Having it simply live
where it's constructed (be it on the stack or as a member
variable) with only the stuff that has to be on the heap on the
heap but have the container have a disabled postblit would
probably be just as useful in most cases, and scoped or region
allactors should be able to help with that. I'm not super
familiar with all of the tricks that they pull to make their
container use efficient, but I have a hard time seeing how
std::vector's semantics are desirable to them. Certainly, when
they care about efficiency to the point that they're using static
arrays everywhere, the fact that vector has its guts on the heap
means that it's in a different class of containers entirely from
static arrays and does not have the same desirable properties,
even if it is a value type like static arrays. And even then, the
fact that static arrays are value types isn't really the gain.
It's more that they're on the stack, and that naturally turns
them into value types.
In any case, I'm pretty sure that we do have a region allocator
in std.experimental.allocator, so if someone wants to try and put
a container that normally lives on the heap on the stack, it
should be possible (at least, with a limited number of elements).
And it might actually be easier to do that with a container
that's entirely on the heap instead of part of it living on the
stack and part of it living on the heap (as occurs with the C++
containers).
If Manu (or others like him) have strong feelings about what
kinds of containers would be best for them, they can speak up and
point out what they need, but I don't see how having value type
containers would generally be useful to them except in the cases
where the container actually, fully lives on the stack (which is
almost never the case), and it seems even more questionable for
everyone else.
- Jonathan M Davis
More information about the Digitalmars-d
mailing list