Section 4 - Using and Benchmarking vectors

2A.4.1 Using FHvector

Once we incorporate the header file FHvector.h into our project, we use it as we would any template. Here are a few simple tests.

// Test and demo of FHvector class template
// CS 2C, Foothill College, Michael Loceff, creator

#include <iostream>
#include <string>
using namespace std;

#include "FHvector.h"

// --------------- main ---------------
#define MAX_SIZE 100000

int main()
{
   int k;
   double avg;

   // use [] to write to a pre-allocated FHvector
   FHvector<int> vectorOf_FH_ints(MAX_SIZE);

   for (k = 0; k < MAX_SIZE; k++)
      vectorOf_FH_ints[k] = k;

   for (k = 0, avg = 0.; k < MAX_SIZE; k++)
      avg += vectorOf_FH_ints[k];
   avg /= MAX_SIZE;
   cout << endl << "The average is: " << avg << endl;
  
   // use push_back() to write to an initially empty FHvector
   FHvector<int> vectorOf_FH_ints2;

   for (k = 0; k < MAX_SIZE; k++)
      vectorOf_FH_ints2.push_back(k);

   for (k = 0, avg = 0.; k < MAX_SIZE; k++)
      avg += vectorOf_FH_ints[k];
   avg /= MAX_SIZE;
   cout << endl << "The average is: " << avg << endl;

   // chop off 50,000 items from the array:
   cout << endl << "Size before: " << vectorOf_FH_ints2.size() << endl;
   vectorOf_FH_ints2.resize(50000);
   cout << endl << "Size after: " << vectorOf_FH_ints2.size() << endl;

   // test front and back
   cout << endl << "First item: " << vectorOf_FH_ints2.front() << endl;
   cout << endl << "Last item: " << vectorOf_FH_ints2.back() << endl;

   // test pop_back() - should leave 30,000 elements in the vector
   for (k = 0; k < 20000; k++)
      vectorOf_FH_ints2.pop_back();
   cout << endl << "Last item after pop_backs: " << vectorOf_FH_ints2.back() << endl;

   // test range checking
   try
   {
      vectorOf_FH_ints2[0] = 27;
      vectorOf_FH_ints2[30001] = 27;
      vectorOf_FH_ints2[0] = -27;     // should never be reached
   }
   catch (FHvector<int>::OutOfBoundsException &e)
   {
      cout << "out-of-bounds detected \n";
   }
   cout << endl << "First item still 27? : " << vectorOf_FH_ints2.front() << endl;

   // test iterators
   FHvector<int>::iterator iter;
   for (iter = vectorOf_FH_ints2.begin(); iter != vectorOf_FH_ints2.end(); iter++)
   {
      if ( *iter % 5000 == 0)
         cout << *iter << endl;
   }

   cout << endl << "------------ end of FHvector test --------------- \n\n";

   return 0;
}

/* -------------------------- RUN --------------------------

The average is: 49999.5

The average is: 49999.5

Size before: 100000

Size after: 50000

First item: 0

Last item: 49999

Last item after pop_backs: 29999
out-of-bounds detected

First item still 27? : 27
5000
10000
15000
20000
25000

------------ end of FHvector test ---------------

Press any key to continue . . .
-------------------------- END RUN -------------------------- */

It works. That's comforting.

This example is very important for you to study because it shows:

2A.4.2 Benchmarking FHvector

We've done benchmarking before, so this is not new. We just apply what we know to the new FHvector container, creating, then computing the average of 100,000 ints. We compare it with raw arrays and STL vectors, remembering that our [] operator incorporates all the protection of the at() function, making an at() function unnecessary for our FHvector template.

Here is the source and the run.

// Analysis of arrays vs. vectors using various access techniques
// CS 2C, Foothill College, Michael Loceff, creator

#include <iostream>
#include <string>
using namespace std;

#include <vector>
#include "FHvector.h"

// for timing our algorithms
#include <time.h>
clock_t startClock();
double computeElapsedTime(clock_t startTime);
void displaySeconds(string message, double time_in_secs);

// --------------- main ---------------
#define ARRAY_SIZE 200000

int main()
{
   int k;
   double avg;
   int arrayOfInts[ARRAY_SIZE];
   vector<int> vectorOfInts(ARRAY_SIZE);
   vector<int> vectorOfInts2;
   FHvector<int> vectorOf_FH_ints(ARRAY_SIZE);
   FHvector<int> vectorOf_FH_ints2;

   clock_t startTime; 
   double elapsedTime;

   startTime = startClock();   // simple array START TIME START TIME ----------
   for (k = 0; k < ARRAY_SIZE; k++)
      arrayOfInts[k] = rand()%100;

   for (k = 0, avg = 0.; k < ARRAY_SIZE; k++)
      avg += arrayOfInts[rand()%ARRAY_SIZE];
   avg/=ARRAY_SIZE;

   cout << endl << avg << endl;
   elapsedTime = computeElapsedTime(startTime);
   displaySeconds("Time for simple array: ", elapsedTime);

   startTime = startClock();   // stl vector [] START TIME START TIME ----------
   for (k = 0; k < ARRAY_SIZE; k++)
      vectorOfInts[k] = rand()%100;

   for (k = 0, avg = 0.; k < ARRAY_SIZE; k++)
      avg += vectorOfInts[rand()%ARRAY_SIZE];
   avg/=ARRAY_SIZE;

   cout << endl << avg << endl;
   elapsedTime = computeElapsedTime(startTime);
   displaySeconds("stl vector using []: ", elapsedTime);

   startTime = startClock();   // stl vector push_back() START TIME START TIME --------
   for (k = 0; k < ARRAY_SIZE; k++)
      vectorOfInts2.push_back(rand()%100);

   for (k = 0, avg = 0.; k < ARRAY_SIZE; k++)
      avg += vectorOfInts2[rand()%ARRAY_SIZE];
   avg/=ARRAY_SIZE;

   cout << endl << avg << endl;
   elapsedTime = computeElapsedTime(startTime);
   displaySeconds("stl vector using push_back(): " , elapsedTime);

   startTime = startClock();   // stl vector at() START TIME START TIME ----------
   for (k = 0; k < ARRAY_SIZE; k++)
      vectorOfInts.at(k) = rand()%100;

   for (k = 0, avg = 0.; k < ARRAY_SIZE; k++)
      avg += vectorOfInts.at(rand()%ARRAY_SIZE);
   avg/=ARRAY_SIZE;

   cout << endl << avg << endl;
   elapsedTime = computeElapsedTime(startTime);
   displaySeconds("stl vector using at(): ", elapsedTime);

   startTime = startClock();   // FHvector [] START TIME START TIME ----------
   for (k = 0; k < ARRAY_SIZE; k++)
      vectorOf_FH_ints[k] = rand()%100;

   for (k = 0, avg = 0.; k < ARRAY_SIZE; k++)
      avg += vectorOf_FH_ints[rand()%ARRAY_SIZE];
   avg/=ARRAY_SIZE;

   cout << endl << avg << endl;
   elapsedTime = computeElapsedTime(startTime); 
   displaySeconds("FHvector using []: ", elapsedTime);
  
   startTime = startClock();   // FHvector push_back() START TIME START TIME ----------
   for (k = 0; k < ARRAY_SIZE; k++)
      vectorOf_FH_ints2.push_back(rand()%100);

   for (k = 0, avg = 0.; k < ARRAY_SIZE; k++)
      avg += vectorOf_FH_ints2[rand()%ARRAY_SIZE];
   avg/=ARRAY_SIZE;

   cout << endl << avg << endl;
   elapsedTime = computeElapsedTime(startTime);
   displaySeconds("FHvector using push_back(): " , elapsedTime);

   return 0;
}

// for timing our algorithms
clock_t startClock()
{
   return clock();
}

double computeElapsedTime(clock_t startTime)
{
   clock_t stopTime = clock();
   return (double)(stopTime - startTime)/(double)CLOCKS_PER_SEC;
}

void displaySeconds(string message, double time_in_secs)
{
   cout << "Elapsed time for " << message 
      << time_in_secs  << " seconds." << endl;
}

/* -------------------------- RUN --------------------------

49.6864
Elapsed time for Time for simple array: 0 seconds.

49.3352
Elapsed time for stl vector using []: 0.015 seconds.

49.112
Elapsed time for stl vector using push_back(): 0.016 seconds.

49.7352
Elapsed time for stl vector using at(): 0.015 seconds.

49.7503
Elapsed time for FHvector using []: 0 seconds.

49.4243
Elapsed time for FHvector using push_back(): 0.016 seconds.
Press any key to continue . . .

-------------------------- END RUN -------------------------- */

Optinimization was on in this trial. We see that there is no discernable different between FHvectors and raw arrays to within 1/1000 of a second for this 200,000 element array run (they are both clocking 0 seconds). Meanwhile Microsoft STL vectors seem to be slower using [], push_back() and at().

For a second comparison, here is the program run on a Unix platform, without optimization, this time using 400,000 elements (twice as many as in Windows, which limited the size of the array to 200,000):

49.4273
Elapsed time for Time for simple array: 0.012241 seconds.


49.5874
Elapsed time for stl vector using []: 0.011233 seconds.


49.4447
Elapsed time for stl vector using push_back(): 0.024066 seconds.


49.5397
Elapsed time for stl vector using at(): 0.016664 seconds.


49.5007
Elapsed time for FHvector using []: 0.015209 seconds.


49.5064
Elapsed time for FHvector using push_back(): 0.018607 seconds.

FHvector is slightly faster in the push_back() race but slightly slower in the bracket, [], race. This remains true as the array size increases.