// Solution 1
#include <stdio.h>
#include <stdlib.h>
#define GRID_WIDTH 200
#define GRID_HEIGHT 200
#define MIN_SIGNAL_STRENGTH 3
typedef struct pixelStruct {
int _x;
int _y;
int _signal_strength;
} t_pixel;
void print_pixel( const char* msg, const t_pixel* ptr ) {
cout << msg << endl << "\tptr = 0x" << hex << ptr << ", x = " << ptr->_x << ", y = " << ptr->_y << ", signal_strength = " << ptr->_signal_strength" << endl;
}
int main() {
t_pixel* pixelArray[GRID_WIDTH][GRID_HEIGHT];
srand ( 21346 );
int w, h;
// create the pixels..
for ( w = 0; w < GRID_WIDTH; w++ )
for ( h = 0; h < GRID_HEIGHT; h++ )
pixelArray[w][h] = malloc(sizeof(t_pixel));
// see what the memory looks like just after allocation.
print_pixel("after allocation, before initialization", pixelArray[0][0]);
// initialize the pixels..
for ( w = 0; w < GRID_WIDTH; w++ ) {
for ( h = 0; h < GRID_HEIGHT; h++ ) {
t_pixel* ptr = pixelArray[w][h];
ptr->_signal_strength = rand()%10;
ptr->_x = w;
ptr->_y = h;
}
}
// see what the memory looks like after initialization.
print_pixel("after allocation AND initialization", pixelArray[0][0]);
// calculate the % of pixels with below par signal strength
int numPixelsWithLowSignal = 0;
for ( w = 0; w < GRID_WIDTH; w++ ) {
for ( h = 0; h < GRID_HEIGHT; h++ ) {
if ( pixelArray[w][h]->_signal_strength < MIN_SIGNAL_STRENGTH )
++numPixelsWithLowSignal;
}
}
cout << (numPixelsWithLowSignal * 100 / (GRID_WIDTH*GRID_HEIGHT)) << "% of pixels have below par signal strength." << endl;
// free up the memory
for ( w = 0; w < GRID_WIDTH; w++ )
for ( h = 0; h < GRID_HEIGHT; h++ )
free(pixelArray[w][h]);
return 0;
}
/*
// OUTPUT:
after allocation, before initialization
ptr = 0x3e3f48, x = 4063800, y = 4063800, signal_strength = 0
after allocation and initialization
ptr = 0x3e3f48, x = 0, y = 0, signal_strength = 9
29% of pixels have below par signal strength.
*/
// Solution 2
#include <Common.h> // attached to article
#define GRID_WIDTH 2000
#define GRID_HEIGHT 2000
#define MIN_SIGNAL_STRENGTH 3
class pixel {
public:
pixel(int x, int y, int signal_strength) {
_signal_strength = signal_strength;
_x = x;
_y = y;
}
int _signal_strength ;
int _x;
int _y;
void print_pixel(const char* msg) const {
cout << msg << endl << "\tptr = " << hex << this << ", x = " << _x
<< ", y = " << _y << ", signal_strength = " << _signal_strength
<< endl;
}
};
//main program to call the array for 4 ints and return average
int main() {
startClock();
srand ( 213462434L );
vector<pixel*> pixelVec ;
// create AND initialize pixels..
for ( size_t w = 0; w < GRID_WIDTH; w++ )
for ( size_t h = 0; h < GRID_HEIGHT; h++ )
pixelVec.push_back( new pixel(w, h, rand()%10) );
printClock("Time for default new operator(): ");
// see what the memory looks like..
pixelVec[0]->print_pixel("after allocation AND initialization");
// calculate the % of pixels with below par signal strength
int numPixelsWithLowSignal = 0;
for ( size_t i = 0; i < pixelVec.size(); i++ )
if ( pixelVec[i]->_signal_strength < MIN_SIGNAL_STRENGTH )
++numPixelsWithLowSignal;
cout << dec << (numPixelsWithLowSignal * 100 / (GRID_WIDTH*GRID_HEIGHT)) << "% of pixels have below par signal strength." << endl;
// cleanup..
for ( size_t i = 0; i < pixelVec.size(); i++ )
delete pixelVec[i];
pixelVec.clear();
printClock("Total time including delete: ");
return 0 ;
}
/*
OUTPUT:
Time for default new operator(): 9937487 micro seconds
after allocation AND initialization
ptr = 0x3e3f10, x = 0, y = 0, signal_strength = 4
29% of pixels have below par signal strength.
Total time including delete: 20218721 micro seconds
*/
new pixel(w, h, rand()%10)is same as:
pixel* ptr = malloc(sizeof(pixel)); // allocatewhich in turn is same as:
ptr->pixel(w, h, rand()%10); // call constructor to initialize
pixel* ptr = malloc(sizeof(pixel));
ptr->_x = w;
ptr->_y = h;
ptr->_signal_strength = rand()%10;
// Solution 3 using placement new operator.
#include <Common.h> // attached to article
#define GRID_WIDTH 2000
#define GRID_HEIGHT 2000
#define MIN_SIGNAL_STRENGTH 3
class pixel {
public:
pixel(int x, int y, int signal_strength) {
_signal_strength = signal_strength;
_x = x;
_y = y;
}
int _signal_strength ;
int _x;
int _y;
void print_pixel(const char* msg) const {
cout << msg << endl << "\tptr = " << hex << this << ", x = " << _x
<< ", y = " << _y << ", signal_strength = " << _signal_strength
<< endl;
}
};
//main program to call the array for 4 ints and return average
int main() {
startClock();
void* pre_allocated_memory = malloc(GRID_WIDTH * GRID_HEIGHT * sizeof(pixel));
srand ( 213462434L );
vector<pixel*> pixelVec ;
// create AND initialize pixels..
for ( size_t w = 0; w < GRID_WIDTH; w++ )
for ( size_t h = 0; h < GRID_HEIGHT; h++ ) {
// pixelVec.push_back( new pixel(w, h, rand()%10) );
pixelVec.push_back( new (pre_allocated_memory) pixel(w, h, rand()%10) );
// as we've used the address pointed by pre_allocated_memory
// make pre_allocated_memory point to next slot
pre_allocated_memory = (pixel*) pre_allocated_memory + 1;
}
printClock("Time for placement new operator(): ");
// see what the memory looks like..
pixelVec[0]->print_pixel("after allocation AND initialization");
// calculate the % of pixels with below par signal strength
int numPixelsWithLowSignal = 0;
for ( size_t i = 0; i < pixelVec.size(); i++ )
if ( pixelVec[i]->_signal_strength < MIN_SIGNAL_STRENGTH )
++numPixelsWithLowSignal;
cout << dec << (numPixelsWithLowSignal * 100 / (GRID_WIDTH*GRID_HEIGHT)) << "% of pixels have below par signal strength." << endl;
// cleanup..
pixelVec.clear();
free(pre_allocated_memory);
printClock("Total time including delete: ");
return 0 ;
}
/*
Time for placement new operator(): 218748 micro seconds
after allocation AND initialization
ptr = 0x4f0020, x = 0, y = 0, signal_strength = 4
29% of pixels have below par signal strength.
Total time including delete: 484370 micro seconds
*/
// cleanup
#include <Common.h> // attached to article
#define GRID_WIDTH 2000
#define GRID_HEIGHT 2000
#define MIN_SIGNAL_STRENGTH 3
class pixel {
ifstream* ptr;
public:
pixel(int x, int y, int signal_strength) {
_signal_strength = signal_strength;
_x = x;
_y = y;
ptr = new ifstream("somefile");
}
~pixel() {
if(NULL != ptr) {
ptr->close();
ptr = NULL;
}
}
void print_pixel(const char* msg) const {
cout << msg << endl << "\tptr = " << hex << this << ", x = " << _x
<< ", y = " << _y << ", signal_strength = " << _signal_strength
<< endl;
}
public:
int _signal_strength ;
int _x;
int _y;
};
//main program to call the array for 4 ints and return average
int main() {
startClock();
void* pre_allocated_memory = malloc(GRID_WIDTH * GRID_HEIGHT * sizeof(pixel));
srand ( 213462434L );
vector<pixel*> pixelVec ;
// create AND initialize pixels..
for ( size_t w = 0; w < GRID_WIDTH; w++ )
for ( size_t h = 0; h < GRID_HEIGHT; h++ ) {
// pixelVec.push_back( new pixel(w, h, rand()%10) );
pixelVec.push_back( new (pre_allocated_memory) pixel(w, h, rand()%10) );
// as we've used the address pointed by pre_allocated_memory
// make pre_allocated_memory point to next slot
pre_allocated_memory = (pixel*) pre_allocated_memory + 1;
}
printClock("Time for placement new operator(): ");
// see what the memory looks like..
pixelVec[0]->print_pixel("after allocation AND initialization");
// calculate the % of pixels with below par signal strength
int numPixelsWithLowSignal = 0;
for ( size_t i = 0; i < pixelVec.size(); i++ )
if ( pixelVec[i]->_signal_strength < MIN_SIGNAL_STRENGTH )
++numPixelsWithLowSignal;
cout << dec << (numPixelsWithLowSignal * 100 / (GRID_WIDTH*GRID_HEIGHT)) << "% of pixels have below par signal strength." << endl;
// cleanup..
// for non-trivial destruction call the destructor explicitly.
for ( size_t i = 0; i < pixelVec.size(); i++ )
pixelVec[i]->~pixel();
pixelVec.clear();
free(pre_allocated_memory);
printClock("Total time including delete: ");
return 0 ;
}
// this loop...
for ( size_t h = 0; h < NUM_OBJECTS; h++ ) {
new (pre_allocated_memory) my_type();
pre_allocated_memory = (my_type*) pre_allocated_memory + 1;
}
// ...will NOT exhaust the pre_allocated_memory as long as
// pre_allocated_memory is allocated using sizeof(). e.g.
void* pre_allocated_memory = malloc(NUM_OBJECTS * sizeof(my_type));
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (1)
Commented:
Firstly, you could probably achieve the same (or quite similar) performance gains by allocating objects rather than pointers to objects in the vector.
My main concern with placement new is that its semantics are very different from normal operator new and a lot of caveats need to be considered. For example, if the constructor of the object being supplanted throws the memory it is being supplanted into will leak because, unlike normal construction semantics, there is nothing to release that memory. This may or may not lead to a problem depending upon how your memory pool is managed.
My point is that placement new requires some very special care when being used and shaving a few microseconds for heap allocations when a different design might achieve the same result is probably not a good enough reason for making use of this feature of C++.
Just my 2 pence worth.