views:

181

answers:

8

I have the following two loops:

#include <iostream>
#include <stdlib.h>
#include <time.h>

using namespace std;
int main(){

    int start=clock();
    for (int i=0;i<100;i++)
        cout<<i<<" "<<endl;
    cout<<clock()-start<<"\n"<<endl;
    cout<<"\n";

    int start1=clock();
    for (int j=0;j<100;++j)
        cout<<j<<" "<<endl;
    cout<<"\n";
    cout<<clock()-start1<<" \n "<<endl;

    return 0;
}

I ran that three times. On the first two runs, the second loop was fastest but, on the third run, the first loop was fastest. What does this mean? Which is better? Does it depend on the situation?

A: 

It means you should use statistical techniques to work out which loop is faster (and I would hope those techniques established that they were pretty much on par with each other).

You have no idea what your CPU is doing over and above the load you're putting it under here.

It could be starting up scheduled tasks, handling interrupts, all sorts of things that would skew your results.

You may well have to do a million runs, toss out the outliers, and average the rest to get a decent sample.

On top of that, a hundred iterations is not much, especially since you're doing function calls to cout which may well swamp the time spent doing the loop control.

When I run checks under UNIX, I don't use elapsed time for this very reason. The system and user time give how many seconds the CPU was in use for the given process regardless of the elapsed time.

paxdiablo
A: 

++i should result in the same machine code as i++ with unused result on any halfway-decent compiler (provided it's not overloaded in a fancy way, which can't be the case for int). And even if it doesn't (you'd need to dig out a pretty stupid compiler from the last century), the difference is so small you should never have to worry about it. Well, there are cases where you do need to squeeze every tiny bit of performance out of it, but the least of us every get into this situation. And even then, the body of the loop has way more optimization potential (even in your oversimplified example - I/O is way more costy than copying a machine word).

Or in one sentence: Premature optimization is the root of all evil.

delnan
+2  A: 

These loops are equivalent for induction variable of int type. The post increment versus pre increment questions has been answered here several times. Try to search the archives a little.

Also, the increment operation takes only a small fraction of time compared to the standard IO. Your test is measuring speed of the IO rather than speed of the increment operations.

wilx
+10  A: 

The running time of your loops is overwhelmingly dominated by input-output operations. This means that the time you observe 1) has nothing to do with the actual performance of the loop (i.e. i++ vs ++j), 2) is pretty much unpredictable and unstable (essentially random).

In other words, your experiment is meaningless. It means absolutely nothing.

Finally, in situations when the result of the built-in ++ operator is not used, there is absolutely no difference between postfix and prefix increment. In any reasonable compiler both of your loops will have absolutely identical performance.

AndreyT
I bet that if he compiles this to assembly language that the two loops will be exactly the same
Jaka
A: 

++i is preincrement
i++ is postincrement

see Operator for details

TimeZlicer
I think he knows that.
GMan
Simple answer. Astute. If you would have added something about what the _meaning_ of pre- vs. post increment, I would have +1d you.
xtofl
+5  A: 

In your case it is probably standard measurement error and it does not matter do you use post-increment or pre-increment. For standard types (int, byte ...) it does not matter.

BUT you should get used to using pre-increment because there are performance implications if you are using them on a Class, depending how those operators are implemented. Post-increment operator i++ has to make a copy of the object

For example:

class integer
{
public:
  integer(int t_value)
    : m_value(t_value)
  {
  }

  int get_value() { return m_value; }

  integer &operator++() // pre increment operator
  {
    ++m_value;
    return *this;
  }

  integer operator++(int) // post increment operator
  {
    integer old = *this; // a copy is made, it's cheap, but it's still a copy
    ++m_value;
    return old; // Return old copy
  }

private:
  int m_value;

Take a look at the following answer

StackOverflow: i++ less efficient than ++i, how to show this?

Robert Vuković
Not only is efficiency affected, it actually _means_ something different.
xtofl
@xtofl: How so? (The discussion is about pre/post increment as a separate expression.)
UncleBens
A: 

From what I see, the only difference in the loops is the pre / post increment of the loop variable. The bulk of the process time will be spent on the cout. Incrementing will be of negligible time in comparison.

nonnb
+2  A: 

Old GCC 3.4.4 does this:

First loop:

.L11:
        cmpl    $99, -8(%ebp)
        jg      .L12
        subl    $8, %esp
        pushl   $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        subl    $12, %esp
        pushl   $.LC0
        subl    $12, %esp
        pushl   -8(%ebp)
        pushl   $_ZSt4cout
.LCFI7:
        call    _ZNSolsEi
        addl    $20, %esp
        pushl   %eax
        call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
        addl    $20, %esp
        pushl   %eax
.LCFI8:
        call    _ZNSolsEPFRSoS_E
        addl    $16, %esp
        leal    -8(%ebp), %eax
        incl    (%eax)
        jmp     .L11
.L12:

Second loop:

.L14:
        cmpl    $99, -12(%ebp)
        jg      .L15
        subl    $8, %esp
        pushl   $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        subl    $12, %esp
        pushl   $.LC0
        subl    $12, %esp
        pushl   -12(%ebp)
        pushl   $_ZSt4cout
.LCFI13:
        call    _ZNSolsEi
        addl    $20, %esp
        pushl   %eax
        call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
        addl    $20, %esp
        pushl   %eax
.LCFI14:
        call    _ZNSolsEPFRSoS_E
        addl    $16, %esp
        leal    -12(%ebp), %eax
        incl    (%eax)
        jmp     .L14
.L15:

Can you find any differences? :) (besides i and j being on different locations on stack -8(%ebp) and -12(%ebp))

Jaka