In one of my application, we wanted to achieve 8000 TPS (Transactions per second) where every second, 8000 session objects will be created and destroyed.
To achieve this performance number, I wanted to optimize memory allocation and deallocation so started looking for existing memory pool class particularly in boost libraries (assuming it would be faster) and I am already using few classes from boost.
I found boost::object_pool. It is a header only file so no need to include link library and I included into my application. At the same time, I used boost::multi_index_container to store session object with multiple indexes. The performance was degrading as number of object increases in the container. I thought that is due to boost multi-index-container.
So I created two separate test applications to measure performance of boost object pool and boost multi-index-container and found that boost object_pool was the bottleneck.
Here is the performance number of destroying 10K, 20K and 30K object from object_pool and using standard c++ delete.
./object_pool_test 10000
Total time to destroy 10000 using delete: 0.3158860
Total time to destroy 10000 from object_pool: 1.447002736 sec
./object_pool_test 20000
Total time to destroy 20000 using delete: 0.6340170
Total time to destroy 20000 from object_pool: 9.624879974 sec
./object_pool_test 30000
Total time to destroy 30000 using delete: 0.10413087
Total time to destroy 30000 from object_pool: 32.150515048 sec
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// File: test_object_pool.cpp
// Description: Test performance of deleting object from boost::object_pool versus standard delete operator.
// Author: Rohit Joshi
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <vector>
#include <time.h>
#include <boost/pool/object_pool.hpp>
class Test
{
char buffer[1024];
unsigned long id;
};
int main(int argc, char** argv) {
if(argc < 2) {
std::cout << "Usage: "<< argv[0] << " NumObjects\n";
return 0;
}
int num_objs = atoi(argv[1]);
/////////////////////////////////////////////////////////////////////////////////////////////////
//test1: using standard delete to delete objects
////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<Test*> vDelTest;
vDelTest.reserve(num_objs);
for(int i = 0; i < num_objs; ++i) {
Test *pTest = new Test();
vDelTest.push_back(pTest);
}
timespec tv;
tv.tv_sec = 0;
tv.tv_nsec = 0;
clock_settime(CLOCK_PROCESS_CPUTIME_ID, &tv);
for(int i = 0; i < num_objs; ++i) {
delete vDelTest[i];
}
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tv);
std::cout << "Total time to destroy "<< num_objs << " using delete: " << tv.tv_sec << "." << tv.tv
_nsec << "\n";
//////////////////////////////////////////////////////////////////////////////////////////////////
//delete using object_pool
/////////////////////////////////////////////////////////////////////////////////////////////////
boost::object_pool<Test> m_oPool;
std::vector<Test*> vTest;
vTest.reserve(num_objs);
for(int i = 0; i < num_objs; ++i) {
Test *pTest = m_oPool.construct();
vTest.push_back(pTest);
}
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tv);
std::cout << "Total time to destroy "<< num_objs << " from object_pool: " << tv.tv_sec << "." << tv.tv_nsec << "\n";
return 0;
}
To compile, copy this source into test_object_pool.cpp
g++ -c test_object_pool.cpp
g++ -o test_object_pool test_object_pool.o -lrt