Jean-Denis Muys
Jean-Denis Muys

Reputation: 6842

allocate but don't construct a large array of C++ objects

In some circumstances, my C++14 program needs a "block" of about 100 millions complex<float>, which requires close to 1 GB of RAM. We can safely assume that the required memory will be available.

However allocating a new std::vector is really slow, because the complex constructor is called 100 millions time. On my machine, the code requires around a full second to initialise the array.

By comparison, calling calloc(), which initialises the allocated memory to zero, with mostly the same effect, will run in a very small number of milliseconds.

It turns out we don't even need this initialisation, because the complex in the large array will be populated shortly later from an external source. I am looking therefore at deferring the construction of the objects in that "block" to a later time, and then construct them directly from the external data source.

So my question is, is there a safe idiomatic and efficient C++ way to do that, perhaps using C++ move semantics along the way? If not, and we decide to simply malloc the memory block, can we then simply reinterpret_cast the memory block to a plain old C array of complex<float>?

Thank you for helping

Jean-Denis Muys

Upvotes: 2

Views: 327

Answers (3)

Adam Yaxley
Adam Yaxley

Reputation: 670

If you define the default constructor for the complex<float> class as empty, which leaves the member variables uninitialized, then there shouldn't be any real difference between the two operations given that compiler optimizations are turned on.

Assuming the below definition for the complex class.

template <typename T>
struct complex
{
  complex() {}; // Empty constructor does nothing
  T a, b;
};

The generated assembly for using vector initialization with x86-64 gcc 6.2 and -O2 enabled is:

std::vector<complex<float>> v(100);

    mov     edi, 800
    call    operator new(unsigned long)
    mov     rdi, rax
    call    operator delete(void*)

And the generated assembly for manually calling malloc and free is:

auto v = malloc(100 * sizeof(complex<float>));
free(v);

    mov     edi, 800
    call    malloc
    mov     QWORD PTR [rsp+8], rax
    mov     rdi, QWORD PTR [rsp+8]
    call    free

As you can see, the vector implementation no longer calls the constructor of complex<float> for each element. The usage of vector is more correct and readable, and also takes advantage of RAII which helps to prevent memory leaks.

Upvotes: 3

Trevir
Trevir

Reputation: 1313

I strongly suggest to stick to c++ and avoid manually managing the memory yourself.

The standard library should be enough. E.g.

std::vector< complex > my_vector;

// Reserve the necessary space without constructing anything
my_vector.reserve( 100'000'000); 

// construct the elements when needed
populate( my_vector ); 

Upvotes: 2

Al Kepp
Al Kepp

Reputation: 5980

So your question is if you can use malloc(). I used the same approach many years ago with my old C++ compiler and it worked. But at the end I had to call free() instead of delete[]. I think this is implementation-specific, so you should try it on your compiler.

Upvotes: 0

Related Questions