0

I am making a test program to measure time for storage of each container. The following is my code for the test.

#include <list>
#include <vector>
#include <iostream>
#include <iomanip>
#include <string>
#include <ctime>
#include <cstdlib>
using namespace std;

void insert(list<short>& l, const short& value);
void insert(vector<short>& v, const short& value);
void insert(short arr[], int& logicalSize, const int& physicalSize, const short& value);

int main() {
    clock_t start, end;
    srand(time(nullptr));

    const int SIZE = 50000;
    const short RANGE = 10000;
    list<short> l;
    vector<short> v;
    short* arr = new short[SIZE];
    int logicalSize = 0;

    // array
    start = clock();
    cout << "Array storage time test...";
    for (int i = 0; i < SIZE; i++) {
        try {
            insert(arr, logicalSize, SIZE, (short)(rand() % (2 * RANGE + 1) - RANGE));
        } catch (string s) {
            cout << s << endl;
            system("pause");
            exit(-1);
        }
    }
    end = clock();
    cout << "Time: " << difftime(end, start) << endl << endl;

    // list
    cout << "List storage time test...";
    start = clock();
    for (int i = 0; i < SIZE; i++) {
        insert(l, (short)(rand() % (2 * RANGE + 1) - RANGE));
    }
    end = clock();
    cout << "Time: " << difftime(end, start) << endl << endl;

    // vector
    cout << "Vector storage time test...";
    start = clock();
    for (int i = 0; i < SIZE; i++) {
        insert(v, (short)(rand() % (2 * RANGE + 1) - RANGE));
    }
    end = clock();
    cout << "Time: " << difftime(end, start) << endl << endl;



    delete[] arr;
    system("pause");
    return 0;
}

void insert(list<short>& l, const short& value) {
    for (auto it = l.begin(); it != l.end(); it++) {
        if (value < *it) {
            l.insert(it, value);
            return;
        }
    }
    l.push_back(value);
}

void insert(vector<short>& v, const short& value) {
    for (auto it = v.begin(); it != v.end(); it++) {
        if (value < *it) {
            v.insert(it, value);
            return;
        }
    }
    v.push_back(value);
}

void insert(short arr[], int& logicalSize, const int& physicalSize, const short& value) {
    if (logicalSize == physicalSize) throw string("No spaces in array.");
    for (int i = 0; i < logicalSize; i++) {
        if (value < arr[i]) {
            for (int j = logicalSize - 1; j >= i; j--) {
                arr[j + 1] = arr[j];
            }
            arr[i] = value;
            logicalSize++;
            return;
        }
    }
    arr[logicalSize] = value;
    logicalSize++;
}

However, when I execute the code, the result seems a little different from the theory. The list should be fastest, but the result said that insertion in the list is slowest. Can you tell me why?

3
  • Hope you are compiling with the optimization flags enabled! Commented Mar 30, 2017 at 22:34
  • In your array test replace the loop with memmove(&arr[i], &arr[i + 1], logicalSize - i); It will make it twice as fast. Commented Mar 30, 2017 at 23:43
  • Small fix: memmove(&arr[i], &arr[i + 1], (logicalSize - i) * sizeof(short)) Commented Mar 30, 2017 at 23:56

2 Answers 2

2

Inserting into a vector or array requires moving everything after it; so if at a random spot, requires an average of 1.5 accesses to each element. 0.5 to find the spot, and 0.5*2 (read and write) to do the insert.

Inserting into a list requires 0.5 accesses per element (to find the spot).

This means the vector is only 3 times more element accesses.

Lists nodes are 5 to 9 times larger than vector "nodes" (which are just elements). Forward iteration requires reading 3 to 5 times as much memory (element 16 bits and pointer 32 to 64 bits).

So the list solution reads/writes more memory! Worse, it is sparser (with the back pointer), and it may not be arranged in a cache-friendly way in memory (vectors are contiguous; list nodes may be a mess in linear space) thus messing with cpu memory cache predictions and loads and etc.

List is very rarely faster than vector; you have to be inserting/deleting many times more often than you iterate over the list.

Finally vector uses exponential allocation with reserved unused space. List allocates each time. Calling new is slow, and often not much slower when you ask for bigger chunks than when you ask for smaller ones. Growing a vector by 1 at a time 1000 times results in about 15 allocations (give or take); for list, 1000 allocations.

Sign up to request clarification or add additional context in comments.

Comments

0

Insertion in a list is blisteringly fast, but first you have to find there you want to insert. This is where list comes out a loser.

It might be helpful to stop and read Why is it faster to process a sorted array than an unsorted array? sometime around now because it covers similar material and covers it really well.

With a vector or array each element comes one after the next. Prediction is dead easy, so the CPU can be loading the cache with values you won't need for a while at the same time as it is processing the current value.

With a list predictability is shot, you have to get the next node before you can load the node after that, and that pretty much nullifies the cache. Without the cache you can see an order of magnitude degradation in performance as the CPU sits around waiting for data to be retrieved from RAM.

Bjarne Stroustrup has a number of longer pieces on this topic. The keynote video is definitely worth watching.

One important take-away is take Big-O notation with a grain of salt because it is measuring a the efficiency of the algorithm, not how well the algorithm takes advantage of the hardware.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.