Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Request för std::vector and possibly others: minimal initalization

127 views
Skip to first unread message

henrikv

unread,
Mar 20, 2012, 2:35:35 AM3/20/12
to
Hello group!

When writing parallel code, I often allocate a vector for results. I
allocate the complete vector with a single resize. What I'd like to do
is to tell the resize not to initialize the content, since this will
be overwritten anyway. I'd figure something like:

struct no_init {} g_no_int;

struct Dummy
{
Dummy(int a_=0) : a(a_) {}
Dummy(const no_init &) {} // Without a v-table, this shouldn't need
to
touch any part of the created object?

int a;
};

void foo()
{
std::vector<Dummy> dummies;
dummies.resize(1000, g_no_int);
// ...
}

Proposal: add a method to std::vector that allows resize with a
different constructor than default or copy
constructor:

template <typename T2>
void resize_construct_using_translation(size_t n, const T2 &t2);

Any thoughts on this?

Best regards,
Henrik Vallgren


--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp...@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]

Francis Glassborow

unread,
Mar 21, 2012, 2:35:07 PM3/21/12
to
On 20/03/2012 06:35, henrikv wrote:
> Hello group!
>
> When writing parallel code, I often allocate a vector for results. I
> allocate the complete vector with a single resize. What I'd like to do
> is to tell the resize not to initialize the content, since this will
> be overwritten anyway.

Isn't that what reserve() is for?

Jason McKesson

unread,
Mar 21, 2012, 2:35:35 PM3/21/12
to
On Monday, March 19, 2012 11:35:35 PM UTC-7, henrikv wrote:
> Hello group!
>
> When writing parallel code, I often allocate a vector for results. I
> allocate the complete vector with a single resize. What I'd like to do
> is to tell the resize not to initialize the content, since this will
> be overwritten anyway. I'd figure something like:
>
> struct no_init {} g_no_int;
>
> struct Dummy
> {
> Dummy(int a_=0) : a(a_) {}
> Dummy(const no_init&) {} // Without a v-table, this shouldn't need
> to
> touch any part of the created object?
>
> int a;
> };
>
> void foo()
> {
> std::vector<Dummy> dummies;
> dummies.resize(1000, g_no_int);
> // ...
> }
>
> Proposal: add a method to std::vector that allows resize with a
> different constructor than default or copy
> constructor:
>
> template<typename T2>
> void resize_construct_using_translation(size_t n, const T2&t2);
>
> Any thoughts on this?
>
> Best regards,
> Henrik Vallgren
>

This looks more like an improper use of `std::vector` than anything that should be added to the specification.

All you need to do is use `std::vector::reserve` to allocate as much space as you plan to use. Then use `emplace_back` to construct each addition to the `vector` in-place.

henrikv

unread,
Mar 23, 2012, 8:09:57 PM3/23/12
to
On 21 mar, 19:35, Francis
Glassborow<francis.glassbo...@btinternet.com> wrote:
> Isn't that what reserve() is for?

What I want to do is to allocate a vector for results calculated in a
parallel loop. Reserve+push_back will not work, since it's not
guaranteed that results will be stored in a predictable order. You
might even get multiple push_back's executed at once, which I suspect
isn't a great idea.

henrikv

unread,
Mar 23, 2012, 8:10:25 PM3/23/12
to
On 21 mar, 19:35, Jason McKesson<jmckes...@gmail.com> wrote:
> This looks more like an improper use of `std::vector` than anything that should be added to the specification.

IMO, telling the vector which constructor to use isn't "improper use".
I'm trying to achieve "minimal but proper initialization", so a class
containing something like a std::string would require that member to
be properly initialized. I can do the same thing today, all that's
required is a default constructor that doesn't touch anything that's
not required:

struct NoInitDummy
{
NoInitDummy() {}

// Make this behave like a Dummy using conversion operators etc
int a;
};

> All you need to do is use `std::vector::reserve` to allocate as much space as you plan to use. Then use `emplace_back` to construct each addition to the `vector` in-place.

Using reserve+emplace_back isn't a good idea. I'm thinking *parallel*
loop here, so neither order, nor race conditions can be predicted.

Kevin McCarty

unread,
Mar 23, 2012, 8:11:22 PM3/23/12
to
On Mar 21, 11:35 am, Jason McKesson<jmckes...@gmail.com> wrote:
> On Monday, March 19, 2012 11:35:35 PM UTC-7, henrikv wrote:
> > Hello group!
>
> > When writing parallel code, I often allocate a vector for results. I
> > allocate the complete vector with a single resize. What I'd like to do
> > is to tell the resize not to initialize the content, since this will
> > be overwritten anyway. I'd figure something like:
[snip]
> > Any thoughts on this?

> This looks more like an improper use of `std::vector` than anything that should be added to the specification.
>
> All you need to do is use `std::vector::reserve` to allocate as much space as you plan to use. Then use `emplace_back` to construct each addition to the `vector` in-place.


There are at least two situations I can think of, where it would be
*very* useful to be able to initialize or resize a vector of PODs such
that the newly allocated elements will have no initialization
performed. (I'm ambivalent as to the exact mechanics Henrik
proposed.)


Case 1) Interfacing with old (C?) code that will initialize a
contiguous block of memory in-place. One can't always expect that
existing code can be rewritten to take a vector and call push_back or
emplace_back on it.

E.g., supposing the existence of something called std::no_initialize
(whatever that might be)

// some old 3rd-party library function; maybe it calls fread()?
extern "C" initialize_mem(size_t sz, int * mem);

vector<int> ints(42, std::no_initialize);
initialize_mem(ints.size(), ints.data());


Case 2) When it is necessary (or at least, easiest) to initialize the
elements in an order other than the consecutive forward traversal you
get with repeated calls to push_back or emplace_back. Consider
setting up a reverse look-up table for instance:

void make_reverse_lookup(const vector<int> & lut, vector<int> &
revlut)
{
// Pre-condition: the contents of 'lut' are the integers
// 0 through lut.size() - 1 in some arbitrary order.
int n = lut.size();
revlut.resize(n, std::no_initialize);

for (int i = 0; i< n; ++i)
revlut[lut[i]] = i;
}


- Kevin B. McCarty

Jason McKesson

unread,
Mar 24, 2012, 8:55:01 PM3/24/12
to
On Friday, March 23, 2012 5:10:25 PM UTC-7, henrikv wrote:
> On 21 mar, 19:35, Jason McKesson<jmckes...@gmail.com> wrote:
> > This looks more like an improper use of `std::vector` than anything that should be added to the specification.
>
> IMO, telling the vector which constructor to use isn't "improper use".
> I'm trying to achieve "minimal but proper initialization", so a class
> containing something like a std::string would require that member to
> be properly initialized. I can do the same thing today, all that's
> required is a default constructor that doesn't touch anything that's
> not required:
>
> struct NoInitDummy
> {
> NoInitDummy() {}
>
> // Make this behave like a Dummy using conversion operators etc
> int a;
> };

How "minimal" do you want? `std::vector::resize(size_t n)` will value-initialize each element. Why do you need it to be *default* initialized?

> > All you need to do is use `std::vector::reserve` to allocate as much space as you plan to use. Then use `emplace_back` to construct each addition to the `vector` in-place.
>
> Using reserve+emplace_back isn't a good idea. I'm thinking *parallel*
> loop here, so neither order, nor race conditions can be predicted.

I didn't say to use `emplace_back` as part of the parallel loop. I meant that you would do that to *initialize* the `vector`, using exactly the constructor you want.

Miles Bader

unread,
Mar 24, 2012, 8:55:26 PM3/24/12
to
Kevin McCarty<kmcc...@gmail.com> writes:
>> All you need to do is use `std::vector::reserve` to allocate as
>> much space as you plan to use. Then use `emplace_back` to construct
>> each addition to the `vector` in-place.
>
> There are at least two situations I can think of, where it would be
> *very* useful to be able to initialize or resize a vector of PODs such
> that the newly allocated elements will have no initialization
> performed. (I'm ambivalent as to the exact mechanics Henrik
> proposed.)

Agreed, this would be a nice feature to have.

It's obviously a more dangerous operation than most, so a suitably
scary name ("resize_uninitialized_yes_yes_I_know_what_Im_doing")
might be called for...

Right now, I just use a wrapper class for the stored element type to
achieve this effect, but that's less flexible and can be awkward.

-Miles

--
Sabbath, n. A weekly festival having its origin in the fact that God made the
world in six days and was arrested on the seventh.

Dave Harris

unread,
Mar 24, 2012, 10:24:35 PM3/24/12
to
henrik....@stream-space.com (henrikv) wrote (abridged):
> IMO, telling the vector which constructor to use isn't "improper
> use".

That's what emplace_back does. It seems to me to be exactly what you
want, except
it adds a single item instead of a range of them. Thus your:

dummies.resize(1000, g_no_init);

would be written:
dummies.reserve(1000);
for (int i = 0; i < 1000; ++i)
dummies.emplace_back(g_no_init);

Either way you'd be relying on the optimiser.


> I'm thinking *parallel* loop here, so neither order, nor race
> conditions can be predicted.

Your resize would need to be done before the parallel code kicked off,
and so would
the above loop. Emplace_back() is not used by the parallel code.

That said, replacing:
resize( size_type count, const value_type &value );

with:
template<class... Args>
void resize( size_type count, Args&&... args );

does seem like a reasonable request to me. It would be useful whenever
constructing
a new value_type with args was preferable to copying a value_type that had been
constructed with args. I don't see a drawback, other than the issues that arise
whenever the standard is changed.

-- Dave Harris, Nottingham, UK.

Daniel Krügler

unread,
Mar 24, 2012, 10:25:02 PM3/24/12
to
Am 24.03.2012 01:09, schrieb henrikv:
>
> On 21 mar, 19:35, Francis
> Glassborow<francis.glassbo...@btinternet.com> wrote:
>>
>> Isn't that what reserve() is for?
>
>
> What I want to do is to allocate a vector for results calculated in a
> parallel loop. Reserve+push_back will not work, since it's not
> guaranteed that results will be stored in a predictable order. You
> might even get multiple push_back's executed at once, which I suspect
> isn't a great idea.


What you describe sounds like a data race to me, so every normal
assumptions have lost their foundation.

Please elaborate in more detail, but speculating upon games with
undefined behaviour doesn't look like something that the Standard
Library should pro-actively support.

HTH & Greetings from Bremen,

Daniel Krügler

Jason McKesson

unread,
Mar 24, 2012, 10:27:45 PM3/24/12
to
On Friday, March 23, 2012 5:11:22 PM UTC-7, Kevin McCarty wrote:
> On Mar 21, 11:35 am, Jason McKesson<jmckes...@gmail.com> wrote:
> > On Monday, March 19, 2012 11:35:35 PM UTC-7, henrikv wrote:
> > > Hello group!
> >
> > > When writing parallel code, I often allocate a vector for results. I
> > > allocate the complete vector with a single resize. What I'd like to do
> > > is to tell the resize not to initialize the content, since this will
> > > be overwritten anyway. I'd figure something like:
> [snip]
> > > Any thoughts on this?
>
> > This looks more like an improper use of `std::vector` than anything that should be added to the specification.
> >
> > All you need to do is use `std::vector::reserve` to allocate as much space as you plan to use. Then use `emplace_back` to construct each addition to the `vector` in-place.
>
>
> There are at least two situations I can think of, where it would be
> *very* useful to be able to initialize or resize a vector of PODs such
> that the newly allocated elements will have no initialization
> performed. (I'm ambivalent as to the exact mechanics Henrik
> proposed.)

Objects cannot have "no initialization performed" on them. An object
either exists (and therefore has been initialized) or it doesn't (and
thus isn't an object; it's a block of memory).

Since you mentioned PODs (more accurately, trivial types), you seem to
be talking about the performance difference between default
initialization (ie: contains undefined stuff) and value initialization
(ie: zeroed out, more or less).

So, at the very least, you would want to call it this:

vector<int> ints(42, std::default_init);

Personally, it's something so minor that I wouldn't really bother with
it. But at the same time, it could be an issue in performance critical
code.

Of course, you still need to solve this problem:

std::vector<decltype(std::default_init)> stuff(42, std::default_init);

Which constructor gets called?

Francis Glassborow

unread,
Mar 24, 2012, 10:28:00 PM3/24/12
to
On 24/03/2012 00:09, henrikv wrote:
>
> On 21 mar, 19:35, Francis
> Glassborow<francis.glassbo...@btinternet.com> wrote:
>>
>> Isn't that what reserve() is for?
>
>
> What I want to do is to allocate a vector for results calculated in a
> parallel loop. Reserve+push_back will not work, since it's not
> guaranteed that results will be stored in a predictable order. You
> might even get multiple push_back's executed at once, which I suspect
> isn't a great idea.
>

If multiple simultaneous push_backs are possible then you will have to
deal with the problem of multiple threads. How are you going to
determine which element of the vector will be assigned to each
result?. And how will you manage to store results computed
concurrently in a predictable order.

Those issues can be dealt with, but being concerned about the cost of
default initialising your vector seems to be unnecessary.

Just make sure that the objects you are creating have a default
constructor that does nothing.

henrikv

unread,
Mar 26, 2012, 2:14:29 AM3/26/12
to
On 25 mar, 02:55, Miles Bader<mi...@gnu.org> wrote:
> Agreed, this would be a nice feature to have.
>
> It's obviously a more dangerous operation than most, so a suitably
> scary name ("resize_uninitialized_yes_yes_I_know_what_Im_doing")
> might be called for...
>
> Right now, I just use a wrapper class for the stored element type to
> achieve this effect, but that's less flexible and can be awkward.
>
> -Miles

Great idea! So one could use:

template <typename T>
struct MinimalConstructible : public T
{
MinimalConstructible() : T(std::use_minimal_initialization) {}
};

//...
std::vector<MinimalConstructible<Dummy> > data;

Henrik


--

Joe keane

unread,
Mar 27, 2012, 3:10:06 PM3/27/12
to
In article<b94ffa35-1289-4a00...@w32g2000vbt.googlegroups.com>,
henrikv<henrik....@stream-space.com> wrote:
>struct NoInitDummy
>{
> NoInitDummy() {}
>
> // Make this behave like a Dummy using conversion operators etc
> int a;
>};

C++ gives some rules about when things get initialized, which usually
helps you, but sometimes just gets in your way. So then you think, 'you
want the one function to run but not the other one?' and then you're
like 'umm, -yeah-, that's what i want'. So then you think, 'is my
design defective?', or is this a case where C++ has inferred what
behavior you want, when actually that is not the behavior you want.

An example is class Y derives from class X. The way to initialize Y, so
we are told, is to call a function to initialize it so far as being an X
object, then call another function to convert an X object to a Y object.
Sometimes this is awkward. Sometimes the easiest thing to do is to say
'the Y initializer will do everything, and please don't talk to the X
initializer at all'. I don't think that is bad design, it's just where
some of the tacit assumptions in the language go a bit awkward; it's not
saying you *can't* do that, that's what an 'uninit' class is there for.

Pedro Lamarão

unread,
Mar 29, 2012, 3:28:07 PM3/29/12
to
Em sexta-feira, 23 de março de 2012 21h11min22s UTC-3, Kevin McCarty escreveu:

> Case 1) Interfacing with old (C?) code that will initialize a
> contiguous block of memory in-place. One can't always expect that
> existing code can be rewritten to take a vector and call push_back or
> emplace_back on it.
>
> E.g., supposing the existence of something called std::no_initialize
> (whatever that might be)
>
> // some old 3rd-party library function; maybe it calls fread()?
> extern "C" initialize_mem(size_t sz, int * mem);
>
> vector<int> ints(42, std::no_initialize);
> initialize_mem(ints.size(), ints.data());

In the OP's use case, there is a known amount of "results" for which space must be allocated.

And it is desired to have space allocated, but no constructors run on it.

Isn't uninitialized-land where operator new and std::allocator live? Why not go there?

--
P.

Daniel Krügler

unread,
Mar 29, 2012, 4:17:58 PM3/29/12
to
Am 29.03.2012 21:28, schrieb Pedro Lamarão:
>
> Em sexta-feira, 23 de março de 2012 21h11min22s UTC-3, Kevin McCarty escreveu:
>
>> Case 1) Interfacing with old (C?) code that will initialize a
>> contiguous block of memory in-place. One can't always expect that
>> existing code can be rewritten to take a vector and call push_back or
>> emplace_back on it.
>>
>> E.g., supposing the existence of something called std::no_initialize
>> (whatever that might be)
>>
>> // some old 3rd-party library function; maybe it calls fread()?
>> extern "C" initialize_mem(size_t sz, int * mem);
>>
>> vector<int> ints(42, std::no_initialize);
>> initialize_mem(ints.size(), ints.data());
>
>
> In the OP's use case, there is a known amount of "results" for which space must be allocated.
>
> And it is desired to have space allocated, but no constructors run on it.
>
> Isn't uninitialized-land where operator new and std::allocator live? Why not go there?


Yes, this is certainly possible. Just provide an allocator, where
construct (and presumably also destroy) is basically a no-op. I
certainly would not recommend this technique generally, because it
requires a cool head and a stable nervous system ;-)

Greetings from Bremen,

Daniel Krügler



0 new messages