Boost C++11 Vector Performance
Given below is the source code that you can use to profile c++ 11 vector performance on your system. You’ll need to download https://github.com/KjellKod/Stopwatch in addition to the code below.
#include "stdafx.h" #include "StopWatch.h" #include <iostream> #include <vector> #include <list> using namespace std; struct BigTestStruct { int iValue = 1; float fValue; long lValue; double dValue; char cNameArr[10]; int iValArr[100]; }; struct TestRunAggregater { double test1; double test2; double test3; void Reset() { test1 = test2 = test3 = 0; } }; void FillVector(vector<BigTestStruct>& testVector); int main() { StopWatch sw; TestRunAggregater tg; tg.test1 = 0; tg.test2 = 0; tg.test3 = 0; // #1: Avoid unnecessary reallocate and copy cycles by reserving the size of vector ahead of time. vector<BigTestStruct> testVector1; vector<BigTestStruct> testVector2; for (int i = 0; i < 100; i++) { sw.Restart(); FillVector(testVector1); tg.test1 += sw.ElapsedUs(); sw.Restart(); testVector2.reserve(10000); FillVector(testVector2); tg.test2 += sw.ElapsedUs(); testVector1.clear(); testVector1.shrink_to_fit(); testVector2.clear(); testVector2.shrink_to_fit(); } cout << "Average Time to Fill Vector Without Reservation:" << (tg.test1 / 100) << endl; cout << "Average Time to Fill Vector With Reservation:" << (tg.test2 / 100) << endl; tg.Reset(); // #2 Use shrink_to_fit() to release memory consumed by the vector – clear() or erase() does not release memory FillVector(testVector1); size_t capacity = testVector1.capacity(); cout << "Capacity Before Erasing Elements:" << capacity << endl; testVector1.erase(testVector1.begin(), testVector1.begin() + 3); // capacity = testVector1.capacity(); cout << "Capacity After Erasing 3 elements Elements:" << capacity << endl; testVector1.clear(); capacity = testVector1.capacity(); cout << "Capacity After clearing all emements:" << capacity << endl; testVector1.shrink_to_fit(); capacity = testVector1.capacity(); cout << "Capacity After shrinking the Vector:" << capacity << endl; // Point # 3: When filling up or copying into a vector, prefer assignment over insert() or push_back(). cout << "Begining Test for Vector element enumeration " << endl; //Using an iterator vector<BigTestStruct> testVectorSum; FillVector(testVectorSum); for (int i = 0; i < 100; i++) { sw.Restart(); int sum = 0; for (auto it = testVectorSum.begin(); it != testVectorSum.end(); ++it) { sum = sum + it->iValue; } tg.test1 += sw.ElapsedUs(); //Using the at() member function sw.Restart(); sum = 0; for (unsigned i = 0; i < testVectorSum.size(); ++i) { sum = sum + testVectorSum.at(i).iValue; } tg.test2 += sw.ElapsedUs(); // Using the subscript notation sw.Restart(); sum = 0; for (unsigned i = 0; i < testVectorSum.size(); ++i) { sum = sum + testVectorSum[i].iValue; } tg.test3 += sw.ElapsedUs(); } cout << "Using Iterator:" << (tg.test1 / 100) << endl; cout << "Using at() :" << (tg.test2 / 100) << endl; cout << "Using subscripting:" << (tg.test3 / 100) << endl; tg.Reset(); // Point # 4: While iterating through elements in a std::vector, avoid the std::vector::at() function vector<BigTestStruct> sourceVector, destinationVector; FillVector(sourceVector); for (int i = 0; i < 100; i++) { // Assign sourceVector to destination vector sw.Restart(); destinationVector = sourceVector; tg.test1 += sw.ElapsedUs(); //Using std::vector::insert() vector<BigTestStruct> sourceVector1, destinationVector1; FillVector(sourceVector1); sw.Restart(); destinationVector1.insert(destinationVector1.end(), sourceVector1.begin(), sourceVector1.end()); tg.test2 += sw.ElapsedUs(); //Using push_back() vector<BigTestStruct> sourceVector2, destinationVector2; FillVector(sourceVector2); sw.Restart(); for (unsigned i = 0; i < sourceVector2.size(); ++i) { destinationVector2.push_back(sourceVector2[i]); } tg.test3 += sw.ElapsedUs(); } cout << "Average of Assigning Vector :" << (tg.test1 / 100) << endl; cout << "Average of Using insert() :" << (tg.test2 / 100) << endl; cout << "Average of Using push_back :" << (tg.test3 / 100) << endl; tg.Reset(); //Point # 5: Don’t use push-front() – its O(n) - if for some reason you need to use push_front(), consider using a std::list vector<BigTestStruct> sourceVector3, pushFrontTestVector; FillVector(sourceVector3); list<BigTestStruct> pushFrontTestList; for (int i = 0; i < 100; i++) { //Push 100k elements in front of the new vector -- this is horrible code !!! sw.Restart(); for (unsigned i = 1; i < sourceVector3.size(); ++i) { pushFrontTestVector.insert(pushFrontTestVector.begin(), sourceVector3[i]); } tg.test1 += sw.ElapsedUs(); // push in front of a list sw.Restart(); for (unsigned i = 0; i < sourceVector3.size(); ++i) { pushFrontTestList.push_front(sourceVector3[i]); } tg.test2 += sw.ElapsedUs(); } cout << "Average of Pushing in front of Vector :" << (tg.test1 / 100) << endl; cout << "Average of Pushing in front of list :" << (tg.test2 / 100) << endl; tg.Reset(); // Point # 6: Prefer emplace_back() instead of push_back() while inserting into a vector vector<BigTestStruct> sourceVector4, pushBackTestVector, emplaceBackTestVector; FillVector(sourceVector4); for (int i = 0; i < 100; i++) { //Test push back performance sw.Restart(); for (unsigned i = 0; i < sourceVector4.size(); ++i) { pushBackTestVector.push_back(sourceVector4[i]); } tg.test1 += sw.ElapsedUs(); //Test emplace_back() sw.Restart(); for (unsigned i = 0; i < sourceVector4.size(); ++i) { emplaceBackTestVector.emplace_back(sourceVector4[i]); } tg.test2 += sw.ElapsedUs(); } cout << "Average Using push_back :" << (tg.test1 / 100) << endl; cout << "Average Using emplace_back :" << (tg.test2 / 100) << endl; return 0; } // Fuctions to show the benefit of vector::reserve() void FillVector(vector<BigTestStruct>& testVector) { for (int i = 0; i < 10000; i++) { BigTestStruct bt; testVector.push_back(bt); } }