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

前端 未结 17 1958
臣服心动
臣服心动 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 18:00
    1. ++i - faster not using the return value
    2. i++ - faster using the return value

    When not using the return value the compiler is guaranteed not to use a temporary in the case of ++i. Not guaranteed to be faster, but guaranteed not to be slower.

    When using the return value i++ allows the processor to push both the increment and the left side into the pipeline since they don't depend on each other. ++i may stall the pipeline because the processor cannot start the left side until the pre-increment operation has meandered all the way through. Again, a pipeline stall is not guaranteed, since the processor may find other useful things to stick in.

    0 讨论(0)
  • 2020-11-21 18:02

    Here's a benchmark for the case when increment operators are in different translation units. Compiler with g++ 4.5.

    Ignore the style issues for now

    // a.cc
    #include <ctime>
    #include <array>
    class Something {
    public:
        Something& operator++();
        Something operator++(int);
    private:
        std::array<int,PACKET_SIZE> data;
    };
    
    int main () {
        Something s;
    
        for (int i=0; i<1024*1024*30; ++i) ++s; // warm up
        std::clock_t a = clock();
        for (int i=0; i<1024*1024*30; ++i) ++s;
        a = clock() - a;
    
        for (int i=0; i<1024*1024*30; ++i) s++; // warm up
        std::clock_t b = clock();
        for (int i=0; i<1024*1024*30; ++i) s++;
        b = clock() - b;
    
        std::cout << "a=" << (a/double(CLOCKS_PER_SEC))
                  << ", b=" << (b/double(CLOCKS_PER_SEC)) << '\n';
        return 0;
    }
    

    O(n) increment

    Test

    // b.cc
    #include <array>
    class Something {
    public:
        Something& operator++();
        Something operator++(int);
    private:
        std::array<int,PACKET_SIZE> data;
    };
    
    
    Something& Something::operator++()
    {
        for (auto it=data.begin(), end=data.end(); it!=end; ++it)
            ++*it;
        return *this;
    }
    
    Something Something::operator++(int)
    {
        Something ret = *this;
        ++*this;
        return ret;
    }
    

    Results

    Results (timings are in seconds) with g++ 4.5 on a virtual machine:

    Flags (--std=c++0x)       ++i   i++
    -DPACKET_SIZE=50 -O1      1.70  2.39
    -DPACKET_SIZE=50 -O3      0.59  1.00
    -DPACKET_SIZE=500 -O1    10.51 13.28
    -DPACKET_SIZE=500 -O3     4.28  6.82
    

    O(1) increment

    Test

    Let us now take the following file:

    // c.cc
    #include <array>
    class Something {
    public:
        Something& operator++();
        Something operator++(int);
    private:
        std::array<int,PACKET_SIZE> data;
    };
    
    
    Something& Something::operator++()
    {
        return *this;
    }
    
    Something Something::operator++(int)
    {
        Something ret = *this;
        ++*this;
        return ret;
    }
    

    It does nothing in the incrementation. This simulates the case when incrementation has constant complexity.

    Results

    Results now vary extremely:

    Flags (--std=c++0x)       ++i   i++
    -DPACKET_SIZE=50 -O1      0.05   0.74
    -DPACKET_SIZE=50 -O3      0.08   0.97
    -DPACKET_SIZE=500 -O1     0.05   2.79
    -DPACKET_SIZE=500 -O3     0.08   2.18
    -DPACKET_SIZE=5000 -O3    0.07  21.90
    

    Conclusion

    Performance-wise

    If you do not need the previous value, make it a habit to use pre-increment. Be consistent even with builtin types, you'll get used to it and do not run risk of suffering unecessary performance loss if you ever replace a builtin type with a custom type.

    Semantic-wise

    • i++ says increment i, I am interested in the previous value, though.
    • ++i says increment i, I am interested in the current value or increment i, no interest in the previous value. Again, you'll get used to it, even if you are not right now.

    Knuth.

    Premature optimization is the root of all evil. As is premature pessimization.

    0 讨论(0)
  • 2020-11-21 18:02

    ++i is faster than i++ because it doesn't return an old copy of the value.

    It's also more intuitive:

    x = i++;  // x contains the old value of i
    y = ++i;  // y contains the new value of i 
    

    This C example prints "02" instead of the "12" you might expect:

    #include <stdio.h>
    
    int main(){
        int a = 0;
        printf("%d", a++);
        printf("%d", ++a);
        return 0;
    }
    

    Same for C++:

    #include <iostream>
    using namespace std;
    
    int main(){
        int a = 0;
        cout << a++;
        cout << ++a;
        return 0;
    }
    
    0 讨论(0)
  • 2020-11-21 18:06

    @Mark: I deleted my previous answer because it was a bit flip, and deserved a downvote for that alone. I actually think it's a good question in the sense that it asks what's on the minds of a lot of people.

    The usual answer is that ++i is faster than i++, and no doubt it is, but the bigger question is "when should you care?"

    If the fraction of CPU time spent in incrementing iterators is less than 10%, then you may not care.

    If the fraction of CPU time spent in incrementing iterators is greater than 10%, you can look at which statements are doing that iterating. See if you could just increment integers rather than using iterators. Chances are you could, and while it may be in some sense less desirable, chances are pretty good you will save essentially all the time spent in those iterators.

    I've seen an example where the iterator-incrementing was consuming well over 90% of the time. In that case, going to integer-incrementing reduced execution time by essentially that amount. (i.e. better than 10x speedup)

    0 讨论(0)
  • 2020-11-21 18:08

    The Google C++ Style Guide says:

    Preincrement and Predecrement

    Use prefix form (++i) of the increment and decrement operators with iterators and other template objects.

    Definition: When a variable is incremented (++i or i++) or decremented (--i or i--) and the value of the expression is not used, one must decide whether to preincrement (decrement) or postincrement (decrement).

    Pros: When the return value is ignored, the "pre" form (++i) is never less efficient than the "post" form (i++), and is often more efficient. This is because post-increment (or decrement) requires a copy of i to be made, which is the value of the expression. If i is an iterator or other non-scalar type, copying i could be expensive. Since the two types of increment behave the same when the value is ignored, why not just always pre-increment?

    Cons: The tradition developed, in C, of using post-increment when the expression value is not used, especially in for loops. Some find post-increment easier to read, since the "subject" (i) precedes the "verb" (++), just like in English.

    Decision: For simple scalar (non-object) values there is no reason to prefer one form and we allow either. For iterators and other template types, use pre-increment.

    0 讨论(0)
  • 2020-11-21 18:09

    The intended question was about when the result is unused (that's clear from the question for C). Can somebody fix this since the question is "community wiki"?

    About premature optimizations, Knuth is often quoted. That's right. but Donald Knuth would never defend with that the horrible code which you can see in these days. Ever seen a = b + c among Java Integers (not int)? That amounts to 3 boxing/unboxing conversions. Avoiding stuff like that is important. And uselessly writing i++ instead of ++i is the same mistake. EDIT: As phresnel nicely puts it in a comment, this can be summed up as "premature optimization is evil, as is premature pessimization".

    Even the fact that people are more used to i++ is an unfortunate C legacy, caused by a conceptual mistake by K&R (if you follow the intent argument, that's a logical conclusion; and defending K&R because they're K&R is meaningless, they're great, but they aren't great as language designers; countless mistakes in the C design exist, ranging from gets() to strcpy(), to the strncpy() API (it should have had the strlcpy() API since day 1)).

    Btw, I'm one of those not used enough to C++ to find ++i annoying to read. Still, I use that since I acknowledge that it's right.

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