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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#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);
}
}
#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); } }
#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);
  }
}