Is there a performance difference between i++ and ++i in C++?

前端 未结 17 2062
臣服心动
臣服心动 2020-11-21 17:15

We have the question is there a performance difference between i++ and ++i in C?

What\'s the answer for C++?

相关标签:
17条回答
  • 2020-11-21 17:51

    Yes. There is.

    The ++ operator may or may not be defined as a function. For primitive types (int, double, ...) the operators are built in, so the compiler will probably be able to optimize your code. But in the case of an object that defines the ++ operator things are different.

    The operator++(int) function must create a copy. That is because postfix ++ is expected to return a different value than what it holds: it must hold its value in a temp variable, increment its value and return the temp. In the case of operator++(), prefix ++, there is no need to create a copy: the object can increment itself and then simply return itself.

    Here is an illustration of the point:

    struct C
    {
        C& operator++();      // prefix
        C  operator++(int);   // postfix
    
    private:
    
        int i_;
    };
    
    C& C::operator++()
    {
        ++i_;
        return *this;   // self, no copy created
    }
    
    C C::operator++(int ignored_dummy_value)
    {
        C t(*this);
        ++(*this);
        return t;   // return a copy
    }
    

    Every time you call operator++(int) you must create a copy, and the compiler can't do anything about it. When given the choice, use operator++(); this way you don't save a copy. It might be significant in the case of many increments (large loop?) and/or large objects.

    0 讨论(0)
  • 2020-11-21 17:52

    Both are as fast ;) If you want it is the same calculation for the processor, it's just the order in which it is done that differ.

    For example, the following code :

    #include <stdio.h>
    
    int main()
    {
        int a = 0;
        a++;
        int b = 0;
        ++b;
        return 0;
    }
    

    Produce the following assembly :

     0x0000000100000f24 <main+0>: push   %rbp
     0x0000000100000f25 <main+1>: mov    %rsp,%rbp
     0x0000000100000f28 <main+4>: movl   $0x0,-0x4(%rbp)
     0x0000000100000f2f <main+11>:    incl   -0x4(%rbp)
     0x0000000100000f32 <main+14>:    movl   $0x0,-0x8(%rbp)
     0x0000000100000f39 <main+21>:    incl   -0x8(%rbp)
     0x0000000100000f3c <main+24>:    mov    $0x0,%eax
     0x0000000100000f41 <main+29>:    leaveq 
     0x0000000100000f42 <main+30>:    retq
    

    You see that for a++ and b++ it's an incl mnemonic, so it's the same operation ;)

    0 讨论(0)
  • 2020-11-21 17:53

    Time to provide folks with gems of wisdom ;) - there is simple trick to make C++ postfix increment behave pretty much the same as prefix increment (Invented this for myself, but the saw it as well in other people code, so I'm not alone).

    Basically, trick is to use helper class to postpone increment after the return, and RAII comes to rescue

    #include <iostream>
    
    class Data {
        private: class DataIncrementer {
            private: Data& _dref;
    
            public: DataIncrementer(Data& d) : _dref(d) {}
    
            public: ~DataIncrementer() {
                ++_dref;
            }
        };
    
        private: int _data;
    
        public: Data() : _data{0} {}
    
        public: Data(int d) : _data{d} {}
    
        public: Data(const Data& d) : _data{ d._data } {}
    
        public: Data& operator=(const Data& d) {
            _data = d._data;
            return *this;
        }
    
        public: ~Data() {}
    
        public: Data& operator++() { // prefix
            ++_data;
            return *this;
        }
    
        public: Data operator++(int) { // postfix
            DataIncrementer t(*this);
            return *this;
        }
    
        public: operator int() {
            return _data;
        }
    };
    
    int
    main() {
        Data d(1);
    
        std::cout <<   d << '\n';
        std::cout << ++d << '\n';
        std::cout <<   d++ << '\n';
        std::cout << d << '\n';
    
        return 0;
    }
    

    Invented is for some heavy custom iterators code, and it cuts down run-time. Cost of prefix vs postfix is one reference now, and if this is custom operator doing heavy moving around, prefix and postfix yielded the same run-time for me.

    0 讨论(0)
  • 2020-11-21 17:54

    An the reason why you ought to use ++i even on built-in types where there's no performance advantage is to create a good habit for yourself.

    0 讨论(0)
  • 2020-11-21 17:55

    [Executive Summary: Use ++i if you don't have a specific reason to use i++.]

    For C++, the answer is a bit more complicated.

    If i is a simple type (not an instance of a C++ class), then the answer given for C ("No there is no performance difference") holds, since the compiler is generating the code.

    However, if i is an instance of a C++ class, then i++ and ++i are making calls to one of the operator++ functions. Here's a standard pair of these functions:

    Foo& Foo::operator++()   // called for ++i
    {
        this->data += 1;
        return *this;
    }
    
    Foo Foo::operator++(int ignored_dummy_value)   // called for i++
    {
        Foo tmp(*this);   // variable "tmp" cannot be optimized away by the compiler
        ++(*this);
        return tmp;
    }
    

    Since the compiler isn't generating code, but just calling an operator++ function, there is no way to optimize away the tmp variable and its associated copy constructor. If the copy constructor is expensive, then this can have a significant performance impact.

    0 讨论(0)
  • 2020-11-21 17:57

    The performance difference between ++i and i++ will be more apparent when you think of operators as value-returning functions and how they are implemented. To make it easier to understand what's happening, the following code examples will use int as if it were a struct.

    ++i increments the variable, then returns the result. This can be done in-place and with minimal CPU time, requiring only one line of code in many cases:

    int& int::operator++() { 
         return *this += 1;
    }
    

    But the same cannot be said of i++.

    Post-incrementing, i++, is often seen as returning the original value before incrementing. However, a function can only return a result when it is finished. As a result, it becomes necessary to create a copy of the variable containing the original value, increment the variable, then return the copy holding the original value:

    int int::operator++(int& _Val) {
        int _Original = _Val;
        _Val += 1;
        return _Original;
    }
    

    When there is no functional difference between pre-increment and post-increment, the compiler can perform optimization such that there is no performance difference between the two. However, if a composite data type such as a struct or class is involved, the copy constructor will be called on post-increment, and it will not be possible to perform this optimization if a deep copy is needed. As such, pre-increment generally is faster and requires less memory than post-increment.

    0 讨论(0)
提交回复
热议问题