Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uninitialized std::complex constructor when using 'new'

In profiling my program I realized that 10% of the code is spent in a stupid std::complex<double>() constructor, using new std::complex<double>[size_of_array].

I have searched through the web and the default constructor for std::complex seems to take as real and imaginary parts the values double(). Since C++ does not initialize double numbers, I wonder why g++ bothers to initialize std::complex with zeros, and whether I could work around this through the whole program in some way (*)

(*) right now I have to special case the functions that create arrays of complex numbers to allocate uninitialized arrays of doubles and recast them as complex.

Edit: as pointed below, it was an oversight on my side. The default constructor has empty constructors for the real and imaginary part (http://en.cppreference.com/w/cpp/numeric/complex/complex)

 complex( const T& re = T(), const T& im = T() );

but the specification then introduces special cases for double

 complex(double re = 0.0, double im = 0.0);

It is this special case that introduces all the overhead, as it bypasses the actual default constructor of 'double' which does nothing (same as for int, long, float, etc).

like image 316
Juanjo Avatar asked Dec 04 '14 09:12

Juanjo


2 Answers

I wonder why g++ bothers to initialize std::complex with zeros

Because the standard says it must do so, the default constructor is declared as:

constexpr complex(double re = 0.0, double im = 0.0);

so it sets both the members to zero.

It is normal for the standard library to safely initialize types, rather than leaving them uninitialized as you get with built-in types such as double and int*, for instance std::vector<double> zero-initializes its elements too if you resize it so that new elements get added. You can control this for vector by not adding elements to the vector until you know what values you want them to have.

One possible workaround for complex is to use a type that doesn't do the initialization:

struct D
{
  D() noexcept { }; // does not initialize val!
  D(double d) noexcept : val(d) { }
  operator double() const noexcept { return val; }
  D& operator=(double d) noexcept { val = d; return *this; }
  double val;
};

Now if you use std::complex<D> the default constructor does nothing. Add explicit to the converting constructor and/or the conversion operator to suit your taste.

like image 123
Jonathan Wakely Avatar answered Nov 01 '22 23:11

Jonathan Wakely


There is an easy way of doing it. If you "reserve" the memory with a std::vector its much faster because it doesn't call the constructor on each element.

ie this:

std::vector< std::complex< double > > vec;
vec.reserve( 256 );
for( int i = 0; i < 256; i++ )
{
    vec.push_back( std::complex< double >( 1, 1 ) );
}

will be significantly faster than this:

std::complex< double >* arr = new std::complex< double >[256];
for( int i = 0; i < 256; i++ )
{
    arr[i]( std::complex< double >( 1, 1 ) );
}
delete[] arr;

because the constructor is only called once in the first example.

It has the added advantage that you have RAII on your side and 'vec' will automatically be released when its out of scope.

like image 22
Goz Avatar answered Nov 02 '22 01:11

Goz