1
\$\begingroup\$

I wrote this simple program that tests how long it takes for a loop to iterate through a vector<unique_ptr<>>. I'm wondering if this a good way to perform such a speed test? Are there better methods to this?

Also, I can't seem to use myVector.max_size()(which is 1073741823) to allocate the maximum amount of entries for a vector. My program crashes even if I go above my current amount of 70 million.

Results:

> Basic took 0ms 
> BasicR took 0ms 
> Range took 0ms 
> Iterator took 0ms
> IteratorR took 36ms

Program:

// main.cpp 
#include <iostream>
#include <memory>
#include <vector>
#include <chrono>
#include <iterator>

namespace LoopType {
    enum Enum {
        Range,
        Basic,
        BasicR,
        Iterator,
        IteratorR
    };
}
class Test {
public:
    explicit Test(const int a) { mA = a; }
    int getNum() { return mA; }
private:
    int mA;
};

typedef std::vector<std::unique_ptr<Test>> TestVector;

// Loop Types
void RangeBasedForLoop(TestVector& vec) {
    for (auto& test : vec) {
        int num = test->getNum();
    }
}
void BasicLoop(TestVector& vec) {
    for (unsigned i = 0; i < vec.size(); ++i) {
        int num = vec[i]->getNum();
    }
}
void BasicLoopR(TestVector& vec) {
    for (unsigned i = vec.size() - 1; i > 0; --i) {
        int num = vec[i]->getNum();
    }
}
void IteratorLoop(TestVector& vec) {
    TestVector::iterator it;
    for (it = vec.begin(); it != vec.end(); ++it) {
        (*it)->getNum();
    }
}
void IteratorLoopR(TestVector& vec) {
    TestVector::reverse_iterator it;
    for (it = vec.rbegin(); it != vec.rend(); ++it) {
        (*it)->getNum();
    }
}

// Testing & Printing
long long TestLoop(TestVector& vec, void(*loop)(TestVector&)) {
    auto t1 = std::chrono::high_resolution_clock::now();
    loop(vec);
    auto t2 = std::chrono::high_resolution_clock::now();
    return std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
}
void PrintLoopTime(LoopType::Enum type, TestVector& vec) {
    switch (type) {
    case LoopType::Basic:
        std::cout << "Basic took " << TestLoop(vec, &BasicLoop) << "ms" << std::endl;
        break;

    case LoopType::BasicR:
        std::cout << "BasicR took " << TestLoop(vec, &BasicLoopR) << "ms" << std::endl;
        break;

    case LoopType::Range:
        std::cout << "Range took " << TestLoop(vec, &RangeBasedForLoop) << "ms" << std::endl;
        break;

    case LoopType::Iterator:
        std::cout << "Iterator took " << TestLoop(vec, &IteratorLoop) << "ms" << std::endl;
        break;

    case LoopType::IteratorR:
        std::cout << "IteratorR took " << TestLoop(vec, &IteratorLoopR) << "ms" << std::endl;
        break;
    }
}

int main() {
    const unsigned long seventyMillion = 70000000UL;
    std::cout << "Creating vector..." << std::endl;
    std::vector<std::unique_ptr<Test>> testVector;
    testVector.reserve(seventyMillion);
    for (unsigned long i = 0; i < seventyMillion; ++i) {
        testVector.emplace_back(std::make_unique<Test>(i));
    }
    std::cout << testVector.size() << " Elements" << std::endl << std::endl;
    std::cout << "Testing loops..." << std::endl << std::endl;

    PrintLoopTime(LoopType::Basic, testVector);
    PrintLoopTime(LoopType::BasicR, testVector);
    PrintLoopTime(LoopType::Range, testVector);
    PrintLoopTime(LoopType::Iterator, testVector);
    PrintLoopTime(LoopType::IteratorR, testVector);

    std::cout << std::endl << "Any key to exit...";
    std::cin.get();
    return EXIT_SUCCESS;
}
\$\endgroup\$
1
  • \$\begingroup\$ When talking performance, you need to mention which optimization level you chose. \$\endgroup\$
    – papagaga
    Commented Feb 9, 2018 at 9:00

1 Answer 1

4
\$\begingroup\$

Most of your loops are executing in no time at all because the optimizer is eliminating them since they have no side effects. You need to do something observable with them, like summing all elements and returning that sum from the function. Declare a new variable int sum = 0 before the loops, replace the int num = lines with sum +=, then return sum; at the end of the function. This will require changing the return type for all of them to int, and changing the type of pointer passed to TestLoop.

std::vector::max_size returns a theoretical maximum, not one determined on your current system specs. In your case, it returns 0x3FFFFFFF, which is the maximum number of 32 bit pointers that can be stored in a 32 bit address program. This max size also doesn't consider any additional storage space required by objects stored within the vector. Every element you have is a unique_ptr, which when allocated will take 4 bytes for the int, plus additional overhead for the memory management system (so at least 12 or more likely 16 bytes).

Your program crashes with more then 70 million elements because it runs out of memory.

\$\endgroup\$
3
  • \$\begingroup\$ I've made those changes and can also confirm my program was taking over 1GB of memory. I have better results now, with the BasicR() loop being the fastest method(230ms). This is really strange to me though because this loop uses indices while a range-based loop(247ms and also the slowest) does not. Perhaps if i average the results of several tests i'll see that the range-based loop is faster. \$\endgroup\$
    – Chris
    Commented Feb 9, 2018 at 2:08
  • \$\begingroup\$ @Lindens The performance is dominated by cache effects. When BasicR runs, the end of the vector is already in the cache, so it gets a performance benefit that the other, forward iterating, loops don't have. (reverse_iterator has additional overhead that detracts from its performance.) \$\endgroup\$ Commented Feb 9, 2018 at 2:18
  • \$\begingroup\$ I re-ran the program and averaged each loop's completion time 100 times, 50mil iterations each. The Basic() loop out-performed the others with 159ms (6ms faster than the reversed loop), the slowest being the RangeBasedForLoop() once again at 173ms. \$\endgroup\$
    – Chris
    Commented Feb 9, 2018 at 3:03

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.