int size = 5;
Type arr[size];
int size = 5;
dynarray<Type> arr(size);
dynarray<Type> GetArray(int size)
{
dynarray<Type> arr(size);
//Fill in arr with stuff.
return arr;
}
unique_ptr<dynarray<Type>> arrPtr = new dynarray<Type>(GetArray(12));
The main issue is this:
dynarray<Type> GetArray(int size)
{
dynarray<Type> arr(size);
//Fill in arr with stuff.
return arr;
}
unique_ptr<dynarray<Type>> arrPtr = new dynarray<Type>(GetArray(12));
So... what happens here? Assuming that elision works in this case, `dynarray`, we have a non-trivial problem. The `dynarray` being heap-allocated is going to have to heap-allocate its own memory. But the `dynarray` being stack allocated, the return value from `GetArray`, will stack-allocate its memory. Which means that it's contents will have to be copied into the heap-allocated `dynarray`.
dynarray<Type> arr = GetArray(12);
unique_ptr<dynarray<Type>> arrPtr = new dynarray<Type>(std::move(arr));
Well, there's the question of whether copy elision can even work with `dynarray`. But beyond that, how is this not a stack object? You can elide the copy from `arr` into the return value, but you can't elide the copy construction from the return value into the `new` expression.
And if the `new` construction is somehow elided, then feel free to turn it into this:
dynarray<Type> arr = GetArray(12);
unique_ptr<dynarray<Type>> arrPtr = new dynarray<Type>(std::move(arr));
The point is that I want to be able to have some way of ensuring that no copying happens in this case. If that were a `vector<Type>`, I would know that there would be no copying.
On Tuesday, April 2, 2013 11:53:07 PM UTC-7, Marc wrote:Le mercredi 3 avril 2013 03:32:33 UTC+2, Nicol Bolas a écrit :The main issue is this:
dynarray<Type> GetArray(int size)
{
dynarray<Type> arr(size);
//Fill in arr with stuff.
return arr;
}
unique_ptr<dynarray<Type>> arrPtr = new dynarray<Type>(GetArray(12));
So... what happens here? Assuming that elision works in this case, `dynarray`, we have a non-trivial problem. The `dynarray` being heap-allocated is going to have to heap-allocate its own memory. But the `dynarray` being stack allocated, the return value from `GetArray`, will stack-allocate its memory. Which means that it's contents will have to be copied into the heap-allocated `dynarray`.
Could you clarify your example? As far as I understand, with copy elision, there is no stack dynarray here. arr is constructed in the return value, which can't be on the stack.
(it would help to have an implementation to play with)
Well, there's the question of whether copy elision can even work with `dynarray`.
But beyond that, how is this not a stack object? You can elide the copy from `arr` into the return value,
but you can't elide the copy construction from the return value into the `new` expression.
The point is that I want to be able to have some way of ensuring that no copying happens in this case. If that were a `vector<Type>`, I would know that there would be no copying.
It seems there's a need for a fixed size array container, [..].
It seems there's a need for a fixed size array container, and a separate stack based allocator. The stack allocator should prevent container copying and moving. To achieve that would require changes to allocator_traits, without breaking standard behaviour.
When I say the allocator would prevent container copying, I mean to the same allocator type. Copying form/to a stack allocator to/from a heap allocator makes sense, moving does not.
I'm all for a fixed size array container, then we can eliminate the need for new[]/delete[]. A stack based allocator, might have more uses than just a stack based array. Splitting the concerns seems more achievable to me.
On Friday, April 5, 2013 5:08:41 AM UTC-7, Paul Tessier wrote:It seems there's a need for a fixed size array container, and a separate stack based allocator. The stack allocator should prevent container copying and moving. To achieve that would require changes to allocator_traits, without breaking standard behaviour.
When I say the allocator would prevent container copying, I mean to the same allocator type. Copying form/to a stack allocator to/from a heap allocator makes sense, moving does not.
I'm all for a fixed size array container, then we can eliminate the need for new[]/delete[]. A stack based allocator, might have more uses than just a stack based array. Splitting the concerns seems more achievable to me.
The problem is that all other containers can grow arbitrarily large. This works against stack allocation. Now, perhaps the idea is that the allocator will allocate from the stack once, and if you try again it goes to heap memory. But ultimately, I think allowing this for other classes is likely to fail. How would it work for any of the node-based containers, for example? They tend to allocate nodes one at a time.
On Friday, April 5, 2013 5:27:33 AM UTC-7, Paul Tessier wrote:
Agreed, but anyone explicitly using a stack allocator, or similar structure must already know the cost and potential down falls. Creating multiple stack arrays has the same chance of failure. All structures using a stack allocator are expected to have limited lifetime and small size.
Basically, you do something like this, you better know what you are doing.
On Saturday, April 20, 2013 4:23:49 PM UTC+1, Marshall wrote:On Apr 20, 2013, at 4:21 PM, Mikhail Semenov <mikhailse...@gmail.com> wrote:
> Why don't we introduce simple dynamic arrays in objects, which can be allocated in the same place as the objects (stack or heap) they belong to?
> For example, like this:
> class A
> {
> double x[];
> public:
> A(std::size_t n):x(n) {}
> ...
> };
What is sizeof(A) ?
To be honest, I don't care what sizeof(a) will be for a dynamically allocated array. But if you want a formal definition, then the sameas invoid f(double a[]);which will be the size of the pointer (in most cases 4 or 8 depending on the system).
On Apr 20, 2013, at 8:49 AM, Mikhail Semenov <mikhailse...@gmail.com> wrote:On Saturday, April 20, 2013 4:23:49 PM UTC+1, Marshall wrote:On Apr 20, 2013, at 4:21 PM, Mikhail Semenov <mikhailse...@gmail.com> wrote:
> Why don't we introduce simple dynamic arrays in objects, which can be allocated in the same place as the objects (stack or heap) they belong to?
> For example, like this:
> class A
> {
> double x[];
> public:
> A(std::size_t n):x(n) {}
> ...
> };
What is sizeof(A) ?To be honest, I don't care what sizeof(a) will be for a dynamically allocated array. But if you want a formal definition, then the sameas invoid f(double a[]);which will be the size of the pointer (in most cases 4 or 8 depending on the system).Let me try again.If I write:A foo[10];How big is foo? How would the compiler allocate space on the stack/globals/wherever for foo?How many elements are there in foo[3] ? Is that what you want? How would you change that?-- Marshall
Marshall Clow Idio Software <mailto:mc...@gmail.com>
By default, in this case the default constructor would be undefined. The used has to provide it if they want:class A
{
double x[];
public:A():x(50) {}
A(std::size_t n):x(n) {}
...
};
Jonathan,I repeat my point: the value is the same as invoid f(double x[]);which is the size of the pointer.
--
Will I have the same "efficiency" in debug mode as for the STL vectors?Here are some benchmarks for the code that reverses an array of 10,000,000 elements:test | Microsoft VS2012 | GCC 4.8.1 (g++)
vector (indexed) | 1.112 sec. | 0.126 sec.
vector+iterators | 21.878 sec. | 0.363 sec.
standard pointers | 0.042 sec. | 0.046 sec.The file is attached.Most of the code people run during development is in debug mode.
(1) Use option "program database" /Z7 instead of /ZI "edit and continue".
(2) All options -> SDL -> NO /-sdl
(3) #define _ITERATOR_DEBUG_LEVEL 0
N3532 lays out a proposal for including a dynamic array type in C++ called `dynarray`. A "dynamic array" is an array who's length is fixed, but is only provided at runtime, rather than static arrays that have compile-time array lengths.
The big selling point of `dynarray`, and the reason it exists, is so that we can have stack-based arrays with runtime-determined sizes. Instead of taking the C99 approach of allowing this:
int size = 5;
Type arr[size];
We would instead allow this:
int size = 5;
dynarray<Type> arr(size);
The dynamic array proposal allows (but does not require) that `arr` in the above case be allocated on the stack. The dynamic array is only allowed to be allocated on the stack when the array itself is on the stack; if you were to do `new dynarray<Type>`, you would have a heap-allocated array. for me. But it isn't mandated by the standard (for fear of causing complex a lot of work for compiler implementers it seems - given that in the proposal it mentions implementation as a separate library rather than deeper integration to provide for stack allocation).
It is also about knowing that the size will not change. If you want to create a shadow array, then it is very handy to know that the size cannot change because if you do not know that, then you need an additional communication channel.
But also, if the dynarray is allocated on the heap, then its storage cannot be allocated on the stack. So the choice of implementation must be present. Prohibiting dynarray from being heap allocated has some undesireable consequences.
> std::dynarray to language runtime arrays shouldbe what std::array
> is to raw ("C-style") arrays - a wrapper exposing an API providing
> better safety, convenience of use, & consistent integration with
> the rest of the library (iterators, algorithms, etc.). Just
> standardised arrays whose elements have automatic storage
> duration, sized at runtime, integrating well with the rest of
> the library. Guaranteed so to be relied upon.
A guarantee here would be counter-productive. First, it delays
the time until the feature is available.
Second, in multi-threaded environments, stack size is often heavily constrained and defined
by the "high water mark" in any thread.
You want the permission to shift the occasional extra-large allocation to the heap in order to get enough threads into the available memory.
If on-stack allocation is important to you, let your vendor know.
On Friday, May 31, 2013 9:26:09 PM UTC+1, Lawrence Crowl wrote:
It is also about knowing that the size will not change. If you want to create a shadow array, then it is very handy to know that the size cannot change because if you do not know that, then you need an additional communication channel.
I don't know what a dynamically allocated dynarray could give me that std::vector can't. I can manage (constrain usage of) a vector in a way that I ensure the size will not change.
Are any gains of dynarray when utilising dynamic storage (vs std::vector) all dependent on the QoI? Is there anything with a std::dynarray that can't be done in using (including how you manage) a std::vector? Other than you "may" get an optimisation (i.e. possible stack usage depending on implementation) when used as an automatic variable.
The second one is terrible, because when a particular system will prevent stack allocation is implementation-dependent. Some systems might allow you to stack allocate a 30,000 integers; some might not. You would have to have two different code-paths for every use of dynarray: one for if it works, and one for if it doesn't.
template<int size>
array<Type, size> GetArray()
{
array<Type, size> arr
//Fill in arr with stuff.
return arr;
}
unique_ptr<array<Type, 12>> arrPtr = new array<Type, 12>(GetArray<12>());
The second one is terrible, because when a particular system will prevent stack allocation is implementation-dependent. Some systems might allow you to stack allocate a 30,000 integers; some might not. You would have to have two different code-paths for every use of dynarray: one for if it works, and one for if it doesn't.
I'm not sure it is actually that terrible how it sounds to you - it's the same thing when you want to have a std::array<int, 30000> - some systems might allow you to do that, some might not, and you will have two different code-paths..
For me, dynarray<int> arr(n); is to int arr[n]; what array<int, 10> arr; is to int arr[10];
Moving on, the case of runtime-const-size stack-allocated container is a different beast, dynarray should not try to solve it by its own, but unique_ptr<dynarray<T>> is what one needs in this case; the same way how unique_ptr<array<T, n>> works for a compile-time-size stack allocated arrays.
On Sunday, June 2, 2013 4:32:14 PM UTC-7, Róbert Dávid wrote:
The second one is terrible, because when a particular system will prevent stack allocation is implementation-dependent. Some systems might allow you to stack allocate a 30,000 integers; some might not. You would have to have two different code-paths for every use of dynarray: one for if it works, and one for if it doesn't.
I'm not sure it is actually that terrible how it sounds to you - it's the same thing when you want to have a std::array<int, 30000> - some systems might allow you to do that, some might not, and you will have two different code-paths..
There's a really big difference here. If it won't let me do `std::array<int, 30000>`, the compiler will tell me. I will know before my application ships. Therefore, I will not need different code-paths; I will simply change my code for the lowest-common-denominator compiler.
#include <array>
int main()
{
std::array<int, 100*1000*1000> a;
return 0;
}
For me, dynarray<int> arr(n); is to int arr[n]; what array<int, 10> arr; is to int arr[10];
Well, there's your problem: that's not what dynarray is for. It's wrong to blame a class for not doing something that it's not trying to do. Rather than trying to convert a class into being what you think it ought to be, you should be asking for a class that has the particular features you want it to have.
Moving on, the case of runtime-const-size stack-allocated container is a different beast, dynarray should not try to solve it by its own, but unique_ptr<dynarray<T>> is what one needs in this case; the same way how unique_ptr<array<T, n>> works for a compile-time-size stack allocated arrays.
No it does not. `unqiue_ptr<array<>>` does not allocate anything on the stack. `array<>` is only allocated on the stack if it is allocated on the stack, and `unique_ptr` cannot store stack-allocated objects. So if you want to create a `unique_ptr<array<>>`, you're going to have to heap allocate that `array`.
2013. június 3., hétfő 11:49:07 UTC+2 időpontban Nicol Bolas a következőt írta:On Sunday, June 2, 2013 4:32:14 PM UTC-7, Róbert Dávid wrote:
The second one is terrible, because when a particular system will prevent stack allocation is implementation-dependent. Some systems might allow you to stack allocate a 30,000 integers; some might not. You would have to have two different code-paths for every use of dynarray: one for if it works, and one for if it doesn't.
I'm not sure it is actually that terrible how it sounds to you - it's the same thing when you want to have a std::array<int, 30000> - some systems might allow you to do that, some might not, and you will have two different code-paths..
There's a really big difference here. If it won't let me do `std::array<int, 30000>`, the compiler will tell me. I will know before my application ships. Therefore, I will not need different code-paths; I will simply change my code for the lowest-common-denominator compiler.
No it doesn't. I just tested it on MSVC11.3 and ICC 2013.3 on Windows 8 and GCC 4.6.1 and on Ubuntu 11.10. None of them told me this is bad, all dies only at runtime:
#include <array>
int main()
{
std::array<int, 100*1000*1000> a;
return 0;
}
For me, dynarray<int> arr(n); is to int arr[n]; what array<int, 10> arr; is to int arr[10];
Well, there's your problem: that's not what dynarray is for. It's wrong to blame a class for not doing something that it's not trying to do. Rather than trying to convert a class into being what you think it ought to be, you should be asking for a class that has the particular features you want it to have.
I beg your pardon, are you saying I cannot use dynarray<int> arr(n) instead of int arr[n], unlike how I can use array<int, 10> instead of int[10]?? What is the point of dynarray then?
Moving on, the case of runtime-const-size stack-allocated container is a different beast, dynarray should not try to solve it by its own, but unique_ptr<dynarray<T>> is what one needs in this case; the same way how unique_ptr<array<T, n>> works for a compile-time-size stack allocated arrays.
No it does not. `unqiue_ptr<array<>>` does not allocate anything on the stack. `array<>` is only allocated on the stack if it is allocated on the stack, and `unique_ptr` cannot store stack-allocated objects. So if you want to create a `unique_ptr<array<>>`, you're going to have to heap allocate that `array`.
Oh my god, sorry, this is a big time mistype, I should not write these mails at 1:30 am.. I meant a heap allocated container. Heap heap heap.
The main purpose of the class is to be able to have it select stack vs. heap allocation based on non-local factors.
2013/6/3 Róbert Dávid <lrd...@gmail.com>:
>
> Alright. So it is not an std::array, that has dynamic size? Why is it called
> dynarray then?
The "dyn" standards for "dynamically allocated", not for resizable.
There was quite amount of discussion of the naming choice, but in the
end "dynarray" won.
> It feels like this class wants to do way too much - I don't have a tool that
> always allocates data on stack, if I wanted to take responsibility for that.
I'm not sure what you are trying to express here.
You should be aware, that there is no guarantee that a array with
runtime-bound will allocate from the stack (8.3.4 p4):
"If the size of the array exceeds the size of the memory available for
objects with automatic storage duration,
the behavior is undefined. It is unspecified whether a global
allocation function (3.7.4) is invoked to
obtain storage for the array."
What I'm curious about is the performance case. The entire justification for all of this is based on the performance benefits of stack allocation, but I'm curious as to how rigorously these benefits were checked and what evidence was submitted to show that the performance of stack allocation was so desirable.
> For a feature whose inclusion brings a bunch of pitfalls by it's
> very nature, and whose inclusion is justified exclusively by
> performance,
Not exclusively by performance. It also places an invariant on
the size of the array. This invariant can be exploited to avoid
worrying that a subroutine changes the size of a vector. Indeed,
I have code that uses dynarray for that purpose.
The problem is that to get the speed, you'd need an allocator with
LIFO properties. But since vectors can be resized, it won't work
with those in general, only in specific use cases.
I think that is partly for two reasons. First, the technology that
goes into implementing dynarray is old. It is a rather unsuprising
compiler optimization. Second, it is a compiler optimization,
and that means there is a much higher bar in doing the testing than
with library changes.
>1. Are you seriously suggesting removing vector's move constructor?
>2. Even if move was disabled, this is still broken, because you don't know if RVO will be used or not.
>I don't see any way to shoehorn this into vector, without significantly complicating both its implementation and its use.I am not suggesting to remove anything. I am suggesting to add an internal flag (on_stack) that is set to true when the allocation is on the stack,and false otherwise.
Imagine a situation with dynamic arrays: they cannot be easily copied.
>2. Even if move was disabled, this is still broken, because you don't know if RVO will be used or not.The return value optimisation will work,
How can an object be destroyed later, if it is on the stack?
Another issue is that the standard allows different allocators anyway, they should work.
>They do work because if the user has a custom allocator, it is their job to ensure that the lifetime of the storage is at least as long as the >lifetime of all containers which use that storage. That would be a change (and a horrible one IMO) to put that burden on all users of >standard containers using the standard allocator.To be honest, I lost you point here. You mean that if different allocators are used, memory handling will be inefficient.
I thick we are talking about the optimisation issues here.
Don't optimise then!.
Look at my move code: if it's originally allocated on the stack: you copy; by what is on the stack you destroy (and assign nullptr to the pointer). If it is allocated on the heap you move. What is the problem?
The compiler is only allowed to do what is legal.
--
Lawrence,
My basic concern about dynamic arrays is that if I define some functions, like that:
std::vector<double> f(const std::vector<double>& a, const std::vector<double>& b);(and there will be lot's of them in the program),
I won't be able to apply them to similar dynamic arrays: they are different types.On the other hand, if I use vectors only, I will be able to apply them.
The solution will be either to avoid dynamic arrays or write templates.
As I've shown in my (quickly patched up) example, there is no problem to allow vectors to provide two kinds of allocation: on the stack or on the heap. Once you start moving/coping/extending, the on-stack data changes to the on-heap one. I used my own stack in the example, but I think using a program stack won't be a problem.
I compiled my example using three different compilers (and in different modes): there was no problem. I understand it's only a small test, but I think the concept is correct.
Mikhail.
Lawrence,
My basic concern about dynamic arrays is that if I define some functions, like that:
std::vector<double> f(const std::vector<double>& a, const std::vector<double>& b);(and there will be lot's of them in the program),
I won't be able to apply them to similar dynamic arrays: they are different types.On the other hand, if I use vectors only, I will be able to apply them.
The solution will be either to avoid dynamic arrays or write templates.
As I've shown in my (quickly patched up) example, there is no problem to allow vectors to provide two kinds of allocation: on the stack or on the heap. Once you start moving/coping/extending, the on-stack data changes to the on-heap one. I used my own stack in the example, but I think using a program stack won't be a problem.
I compiled my example using three different compilers (and in different modes): there was no problem. I understand it's only a small test, but I think the concept is correct.
I get a "true" output with both clang and GCC.