1

I have a piece of code that creates thousand of objects, and appends them to a vector. The following code is just an example of what is being done, even though the constructor has some parameters, and the for does not actually have that condition, but it serves the purpose of showing that it runs thousands of times.

vector<VolumeInformation*> vector = vector<VolumeInformation*>();
for (int i = 0; i < 5000; ++i) {
    VolumeInformation* info = new VolumeInformation();
    vector.push_back(info);
}

The code takes a lot of time to run, and I was trying to find a faster way of creating all the objects. I read about block allocators, but I am unsure if this is really meant for what I am trying to do, and if it really helps on getting this done faster. I would want to allocate memory for a thousand objects (for example), and keep on using that memory while it is still available, and then allocate some more when needed, avoiding having to allocate memory for a single object every time. Can this be done? Can you point me to somewhere where I can find an example on how to tell 'new' to use the previously allocated memory? If not for the objects itself, can the allocator be used for the memory of the vector (even though the object is what really needs speeding up)?

Thank you.

** UPDATE **

After all the answers and comments, I decided making a change in the code, so the vector would store the objects instead of the pointers, so I could use reserve to pre-allocate some memory for the vector, allowing to save some time by allocating memory for several object instances at once. Although, after doing some performance benchmark, I verify that the change I made is performing much worse, unless I know, ahead of time, the exact size of the vector. Here are my findings, I was wondering if someone could shed light into this, letting me know why this happens, if I am missing something here, or if the approach I was using before is really the best one.

Here is the code I used for benchmarking:

    vector<int> v = vector<int>();
    v.push_back(1);
    v.push_back(3);
    v.push_back(4);
    v.push_back(5);
    v.push_back(7);
    v.push_back(9);

    int testAmount = 200000;
    int reserve = 500000;

    Stopwatch w = Stopwatch();





    w = Stopwatch();
    vector<VolumeInformation> infos = vector<VolumeInformation>();
    infos.reserve(reserve);
    for (int i = 0; i < testAmount; ++i) {
        infos.emplace_back(&v, 1, 0, 0);
    }
    int elapsed = w.Elapsed();






    w = Stopwatch();
    vector<VolumeInformation*> infoPointers = vector<VolumeInformation*>();
    infoPointers.reserve(reserve);
    for (int i = 0; i < testAmount; ++i) {
        infoPointers.emplace_back(new VolumeInformation(&v, 1, 0, 0));
    }
    int elapsed2 = w.Elapsed();

If I comment out both reserve() lines, the version without pointers takes 32.701 seconds, while the pointer version takes 6.159! It takes 5+ times less than using a vector of objects.

If I use reserve, but set the amount of items to reserve to a value lower than the number of iterations, the vector of objects version still takes more time than the pointer version.

If I use reserve with a value higher or equal to the amount of iterations, the vector of objects version becomes a lot faster, taking only 270ms, against 8.901 seconds of the pointer version. The main issue here is that I do not know in advance the size that the vector will reach, as the iterations are not based in a hardcoded number, this was only to do the benchmarking.

Can someone explain why this happens, if there is another way around this, or if I am making anything wrong here?

Miguel Mesquita Alfaiate
  • 2,851
  • 5
  • 30
  • 56
  • ***The code takes a lot of time to run, and I was trying to find a faster way of creating all the objects*** Are you running a release build? I have seen on Visual Studio debug builds 100 times slower than Release in some cases. – drescherjm Sep 15 '14 at 15:19
  • 4
    You're potentially resizing the vector on every iteration - try `vector.reserve(5000)` before the loop. – Paul R Sep 15 '14 at 15:19
  • What do you put in the vector: the whole object or a pointer to it? Your code snippet does not make it clear... (maybe info should be a pointer?) – Emanuele Paolini Sep 15 '14 at 15:21
  • 3
    `VolumeInformation info = new VolumeInformation();` either you are missing a very important `*`, or something is very wrong with your code. – crashmstr Sep 15 '14 at 15:22
  • Please fix the code example so it doesn't look like Java i.e. decide whether you're dealing with pointers or not, and stop using `new` if it's not needed. – Jonathan Wakely Sep 15 '14 at 15:25
  • 1
    `The code takes a lot of time to run` Allocating 5,000 objects and calling push_back on a pointer value 5,000 times takes a long time? If so, you either are 1) timing a build that is unoptimized and doing iterator/pointer checking, or 2) your `VolumeInformation` type has serious construction issues, or 3) your compiler stinks at optimizing code. Seriously, 5,000 is a relatively tiny amount -- 500,000 or 5,000,000 objects, now you're talking. Of course, your code would be faster if you stopped calling the allocator 5.000 times and instead stored objects. – PaulMcKenzie Sep 15 '14 at 15:34
  • I have just edited the code. As for the questions: I am running release, I have also seen that Visual Studio makes some crappy debug builds :) the vector is a vector of pointers, but what is taking more time is the object creation and not the vector push_back – Miguel Mesquita Alfaiate Sep 15 '14 at 15:34
  • @PaulMcKenzie the object is complex and has many things that need to be initialized. Nonetheless, regardless of the lack of optimization Visual Studio might be performing, or the compiler optimizations that might not be there, there is always a better and worse manner of implementing things. I just wish to know if there is a better way :) – Miguel Mesquita Alfaiate Sep 15 '14 at 15:38
  • @BlunT - Well, the problem isn't vector, because all vector is doing is calling `push_back` on a pointer value. Doing this 5,000 times is basically nothing in the grand scheme of things. What the issue then is your VolumeInformation object and the time it takes to construct one. Maybe you should concentrate on that. – PaulMcKenzie Sep 15 '14 at 15:44
  • @PaulMcKenzie That is exactly why I was wondering if there was a way of pre-allocating memory for the objects, since it would make a lot of difference when thousands of them are created... – Miguel Mesquita Alfaiate Sep 15 '14 at 15:47
  • @BlunT - I suggest you look at the answers given by others and try them. IMO, introducing different allocators should be done *only* when all other means of creating thousands of objects deem to be slow. You should also investigate as to the exact reasons why your object takes a long time to create. Maybe you can optimize those routines (or not even call them until the object really needs them). – PaulMcKenzie Sep 15 '14 at 15:57
  • There's some kind of conversion operator at work, converting a `vector*` to a VolumeInformation. Clearly it is expensive, probably because it is copying the vector. – Hans Passant Sep 16 '14 at 09:52

2 Answers2

2

You probably want to reserve space for your 5000 elements ahead of the loop:

vector.reserve(5000);
for (int i = 0; i < 5000; ++i) {
    VolumeInformation info = new VolumeInformation();
    vector.push_back(info);
}

this could save time by eliminating severals resizes as vector grows and if VolumeInformation costs a lot (in time) to copy.

Paul R
  • 208,748
  • 37
  • 389
  • 560
Paul Evans
  • 27,315
  • 3
  • 37
  • 54
  • The main timing issue lies on the object creation and not the vector, that is why I was negleting vector optimizations. But I will give this a try, as it might shave some microseconds of the equation... – Miguel Mesquita Alfaiate Sep 15 '14 at 15:43
  • I upvoted your answer, but did not mark it as correct, as it does not answer the usage of a block allocator (even though I am not even sure block allocators can be used for 'new', or just for allocating space for the vector container). – Miguel Mesquita Alfaiate Sep 15 '14 at 16:39
  • Can you take a look at the updated code, and tell me why I am getting such disparities in times? – Miguel Mesquita Alfaiate Sep 16 '14 at 08:45
2

vector is perfectly capable of pre-allocating a large block and using it for all the elements, if you just use it correctly:

// create 5000 default-constructed X objects
std::vector<X> v(5000);

Or if you need to pass constructor arguments:

std::vector<X> v;
v.reserve(5000);    // allocate block of memory for 5000 objects
for (int i=0 ; i < v.size(); ++i)
  v.emplace_back(arg1, arg2, i % 2 ? arg3 : arg4);

The last line constructs an X in the pre-allocated memory, with no copying, passing the function arguments to the X constructor.

I would want to allocate memory for a thousand objects (for example), and keep on using that memory while it is still available, and then allocate some more when needed, avoiding having to allocate memory for a single object every time.

std::vector does that automatically, you should probably stop using new and just have a vector<VolumeInformation> and put objects into it directly, instead of allocating individual objects and storing pointers to them.

Memory allocation is slow (see Why should C++ programmers minimize use of 'new'?), so stop allocating individual objects. Both the examples above will do 1 allocation, and 5000 constructor calls. Your original code does at least 5001 allocations and 5000 constructor calls (in typical C++ implementations it would do 5013 allocations and 5000 constructor calls).

** UPDATE **

If I comment out both reserve() lines, the version without pointers takes 32.701 seconds, while the pointer version takes 6.159! It takes 5+ times less than using a vector of objects.

Since you haven't actually shown a complete working program you're asking people to guess (always show the actual code!) but it suggests your class has a very slow copy constructor, which is used when the vector grows and the existing elements need to be copied over to the new memory (and the old elements are then destroyed).

If you can add a noexcept move constructor that is more efficient than the copy constructor then std::vector will use that when the vector needs to grow and will run much faster.

The main issue here is that I do not know in advance the size that the vector will reach, as the iterations are not based in a hardcoded number, this was only to do the benchmarking.

You could just reserve more elements than you are ever likely to need, trading higher memory usage for better performance.

Community
  • 1
  • 1
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • I might be missing something here, but my main issue is that my vector size is dynamic. It can either have a dozen elements, or several thousands. I would want to keep reserving space, but I will try out this option and benchmark it. – Miguel Mesquita Alfaiate Sep 15 '14 at 15:41
  • So call `v.reserve(100)` initially, that will still be an improvement on starting with an empty vector and growing from zero. More importantly, stop using `new` to create your objects. Real C++ programmers will point and laugh at you for using `new` like that. – Jonathan Wakely Sep 15 '14 at 15:43
  • Honestly I don't get why there is such a fuss around using new and delete. I do not want C++ to do weird things with my memory when it goes out of scope :) but I will try starting the vector with an initial size, and check if it helps. – Miguel Mesquita Alfaiate Sep 15 '14 at 15:50
  • 2
    @BlunT if you know at runtime how large it will need to be, then call `v.reserve(n)` with that value. The code you supplied in the question has `5000` hard-coded, so that would be where the 5000 in this answer comes from. Vector doubles its storage when it runs out of space, so starting with a realisticly large reserve call can really reduce the amount of allocation that it performs. – Baldrickk Sep 15 '14 at 15:56
  • 1
    @BlunT C++ won't do wierd things with your memory, just that if something is allocated on the stack, then it 'vanishes' when it goes out of scope. If you are calling `new` to get the same effect as global variables then _please stop doing that_. With a vector, as long as the vector is accessable, all your data is too. With `new` and `delete` if you make a mistake, then wierd things (memory leaks etc) **will** start to happen. – Baldrickk Sep 15 '14 at 16:00
  • @Baldrickk I didn't know vector would double its size when needed. This might help a bit, as I can allocate some memory without it being too much, and cut some time whenever it would need resizing. As for the size, I do not know at runtime, and the hardcoded value was just an example. – Miguel Mesquita Alfaiate Sep 15 '14 at 16:03
  • @JonathanWakely I tried changing my code and using reserve, can you please take a look at the updated question and give me some insights on the times I got? – Miguel Mesquita Alfaiate Sep 16 '14 at 08:53
  • @JonathanWakely you're quick :) the constructor of the class is indeed slow. I will take a look into that 'nowxcept' thing, as I do not know what that is! I have already tried reserving a bigger amount than what I usually need, and it does decrease the time, I just have to make sure it does not impact any other stuff needing memory. – Miguel Mesquita Alfaiate Sep 16 '14 at 08:53