Allocating a large multidimensional vector causes memory "overshoot"

Viewed 92

I observed an unexpected behavior when using a large multidmensional std::vector variable. In particular, when I allocate a 4-dimensional vector of large size, it can be seen, using a memory profiler, that my task shortly uses more memory than it would require for the variable.

An MWE to reproduce this behavior is the following:

#include <vector>
#include <iostream>

int main(int argc, char *argv[]){

    getchar();
    std::vector<std::vector<std::vector<std::vector<int64_t>>>> F(1, std::vector<std::vector<std::vector<int64_t>>>(15, std::vector<std::vector<int64_t>>(256, std::vector<int64_t>(100000, 0))));
    getchar();
}

The figure below shows the memory used by the task. The size of the array would require roughly 3GB, however the memory usage overshoots shortly to almost 6GB. Notice that the last dimension of the vector is the largest in size, meaning that the memory overhead from the vector class is relatively small. Memory profile

This has been observed under Manjaro on a 64bit system, compiled with g++ (gcc version 11.1.0 (GCC)). I used psrecord to record the memory usage. Interestingly, I could not reproduce the same behavior for lower dimensional vectors. Even if I remove only the first dimension (which has size 1) I could not reproduce this overshoot on my system.

Does anyone have an idea where this comes from or if this can be avoided (without using a large 1 dimensional vector)? The main problem with this is that I am operating close to the maximum memory provided by my system, meaning that the short overshoot causes memory swapping.

I'll be glad about any ideas and comments. Thanks in advance.

2 Answers

This could be caused for different reasons, like returning without using smart-pointer (which could cause a copy).

But in your particular case, you pass something to F variable's constructor (no matter what), which takes additional memory till constructor returns.

Oh! yet another possible cause, an (un)intelligent vector implementation should reserve more than required (just in case, to speed up later appending).

Example:

Below is what fixed OP's issue:

#include <vector>
#include <iostream>

int main(int argc, char *argv[]){
    getchar();

    std::vector<std::vector<std::vector<std::vector<int64_t>>>> F(
        1, std::vector<std::vector<std::vector<int64_t>>>(15, std::vector<std::vector<int64_t>>(256))
    );
    for (int i = 0; i < 15; i++)
        for (int j = 0; j < 256; j++)
            F[0][i][j] = std::vector<int64_t>(100000, 0);

    getchar();
}

Additional to the comments of @amon, @NathanOliver and @Top-Master that explain the origin of the problem, I will provide a code example that solves the issue.

#include <vector>
#include <iostream>

int main(int argc, char *argv[]){

    getchar();
    std::vector<std::vector<std::vector<std::vector<int64_t>>>> F(1, std::vector<std::vector<std::vector<int64_t>>>(15, std::vector<std::vector<int64_t>>(256)));
    for (int i=0; i<15; i++) for (int j=0; j<256; j++) F[0][i][j] = std::vector<int64_t>(100000, 0);
    getchar();
}
Related