Performance Analysis of Prefix vs. Postfix Increment in C++

1. Example of Prefix/Postfix Increment Operators

The following example demonstrates the implementation of prefix and postfix increment operators for a custom Integer class.

class Integer
{
public:
    // ++i: first increment, then return the new value
    Integer &operator++()
    {
        value_ += 1;
        return *this;
    }

    // i++: first save the old value, then increment, finally return the old value
    Integer operator++(int)
    {
        Integer old = *this;
        value_ += 1;
        return old;
    }

private:
    int value_;
};

2. Performance Tests on Built-in and User-Defined Types

The following code tests the performance difference between prefix and postfix increment on both int and std::vector iterators.

#include <iostream>
#include <vector>
#include <windows.h>

int main()
{
    const int sizeInt = 0x00fffffe;
    const int sizeVec = 0x000ffffe;

    LARGE_INTEGER frequency;
    QueryPerformanceFrequency(&frequency);

    // Test on int array using postfix increment
    {
        int* testValue = new int[sizeInt];

        LARGE_INTEGER start, stop;
        QueryPerformanceCounter(&start);
        for (int i = 0; i < sizeInt; ++i)
        {
            testValue[i]++;
        }
        QueryPerformanceCounter(&stop);

        const auto interval = static_cast<double>(stop.QuadPart - start.QuadPart);
        const auto timeSpan = interval / frequency.QuadPart * 1000.0; // milliseconds
        std::cout << "i++ " << sizeInt << " times takes " << timeSpan << "ms." << std::endl;

        delete[] testValue;
    }

    // Test on int array using prefix increment
    {
        int* testValue = new int[sizeInt];

        LARGE_INTEGER start, stop;
        QueryPerformanceCounter(&start);
        for (int i = 0; i < sizeInt; ++i)
        {
            ++testValue[i];
        }
        QueryPerformanceCounter(&stop);

        const auto interval = static_cast<double>(stop.QuadPart - start.QuadPart);
        const auto timeSpan = interval / frequency.QuadPart * 1000.0; // milliseconds
        std::cout << "++i " << sizeInt << " times takes " << timeSpan << "ms." << std::endl;

        delete[] testValue;
    }

    // Test on vector iterator using postfix increment
    {
        const std::vector<int> testVec(sizeVec);
        LARGE_INTEGER start, stop;
        QueryPerformanceCounter(&start);
        for (auto iter = testVec.cbegin(); iter != testVec.cend(); iter++)
        {
        }
        QueryPerformanceCounter(&stop);

        const auto interval = static_cast<double>(stop.QuadPart - start.QuadPart);
        const auto timeSpan = interval / frequency.QuadPart * 1000.0; // milliseconds
        std::cout << "iterator++ " << sizeVec << " times takes " << timeSpan << "ms." << std::endl;
    }

    // Test on vector iterator using prefix increment
    {
        const std::vector<int> testVec(sizeVec);
        LARGE_INTEGER start, stop;
        QueryPerformanceCounter(&start);
        for (auto iter = testVec.cbegin(); iter != testVec.cend(); ++iter)
        {
        }
        QueryPerformanceCounter(&stop);

        const auto interval = static_cast<double>(stop.QuadPart - start.QuadPart);
        const auto timeSpan = interval / frequency.QuadPart * 1000.0; // milliseconds
        std::cout << "++iterator " << sizeVec << " times takes " << timeSpan << "ms." << std::endl;
    }

    return 0;
}

3. Results from Five Runs

The images below show the results from five consecutive runs on the same machine. (Note: Images are omitted in this text version for brevity, but the trend is consistent across all runs.)

  • Run 1: [results]
  • Run 2: [results]
  • Run 3: [results]
  • Run 4: [results]
  • Run 5: [results]

4. Analysis and Conclusion

From test results, we can observe:

  • For built-in int type, the time taken by prefix increment (++i) and postfix increment (i++) is nearly identical.
  • For std::vector iterators, prefix increment (++iter) takes roughly half the time of postfix increment (iter++).

This difference arises because postfix increment must:

  1. Create a copy of the original object.
  2. Increment the original object.
  3. Return the saved copy (the old value).

Each postfix operation thus involves constructing a temporary object, which adds overhead for user-defined types. For built-in types, the compiler can often optimize away the copy, making the performance equal.

Conclusion:

  • For C++ built-in types (like int), prefix and postfix increment have negligible performance difference.
  • For user-defined types (classes, structs, or iterators), prefix increment is approximately twice as efficient as postfix increment.

5. Important Notes

The test loops above, if compiled in Release mode, may be optimized away entirely by the compiler because the results of the increment operations are not used. To observe the expected performance difference, the code should be compiled in Debug mode. However, in real-world applications, the compiler typically does not eliminate the increment operations if their results are actually used.

Tags: C++ Performance increment prefix postfix

Posted on Mon, 24 Aug 2026 16:06:59 +0000 by ghadacr