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
4 comments:
By the way, I found open ticket in boost forum for this issue.
https://svn.boost.org/trac/boost/ticket/3789
.
After the bug fix "71842" was implemented (https://svn.boost.org/trac/boost/changeset/78412), have you revisited the object pool solution using unordered?
.
In my tests, boost::object_pool begin outperforms traditional new/delete by about 2:1 in almost all scenarios, but *only* after enabling compiler optimizations. Until then, the performance is roughly the same (clang on OS-X 10.8.2). To make things more interesting and demonstrate abstract usage patterns, I change the test so that every other object gets append, the remaining objects are std::shared_ptr and are either delete'ed or pushed back to the end of the vector. Not great, but a more realistic demo than just a loop.
There is so much wrong with this, you are missing the loop to actually destroy the pool objects, the 2nd timing functions is measuring the total program time including the first test and the boost pool allocation time. Your program doesn't even measure the thing it was designed to measure.
Post a Comment