i++ vs ++i

Started by
39 comments, last by Raghar 18 years, 8 months ago
Quote:Original post by pi_equals_3
Quote:Original post by Inmate2993
Any technical interviewer that thought less of me because I used one over the other, I'll tell him to shove that job right up his ass.


Edit: Of course, in most cases I can imagine, I would rather have a programmer who knew how to program well over a programmer who knew everything about a certain language.


Because people who know everything about a certain language can't program well?

Most people who program well do know everything about a certain language. I dont know what you're trying to say with your last statement there...
Advertisement
Quote:Original post by Nitage
Quote:Original post by Nypyren
If you don't use the intermediate value of 'var++', the compiler removes all the unnecessary code... even in the case of complex stuff like iterators.

This assumes you have a 'good' optimizer.

Yes, compilers ARE this smart. Assembly-level dependency graphing is freaking awesome.

Just remember, when you're looking at assembly, if you're using a debug build, you're probably not seeing optimized results.


operator++(int) and operator++() could be overloaded to do totally different things, so a compiler cannot optimize this away in all cases.


Quoted for emphasis, and also to give an example:

class foo{                                                                       int i;                                                                        public:  foo() : i(0) {};  foo& operator++() {    i = i + 1;    cout << "This is clever" << endl;    return *this;  }  foo operator++( int d ) {    i = i + 1;    cout << "This is not clever" << endl;    return *this;  }};                                                                     


This optimization is simply not doable at linking time because of this case.
Quote:Original post by Inmate2993
*** Source Snippet Removed ***

All 4 types of increment on gcc compilers are equivalent. Any technical interviewer that thought less of me because I used one over the other, I'll tell him to shove that job right up his ass.
Please people, when doing benchmarks you must at least use the values, or the compiler could remove the variable altogether. Or in this case, it could have combined the increments together into one increment. Better to do this:
int main(int argc, char **argv){  int i = rand();  cout << i << endl;  i++;  cout << i << endl;  ++i;  cout << i << endl;  i += 1;  cout << i << endl;  i = i + 1;  cout << i << endl;  return 0;}
Though as noted, this test is pointless, as we know they are the same for integers.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Quote:Original post by iMalc
Please people, when doing benchmarks you must at least use the values, or the compiler could remove the variable altogether. Or in this case, it could have combined the increments together into one increment. Better to do this:*** Source Snippet Removed ***Though as noted, this test is pointless, as we know they are the same for integers.


He showed you the disassembly output. The incresments WERE there. They were just identical instrucion-wise.
"C lets you shoot yourself in the foot rather easily. C++ allows you to reuse the bullet!"
i used to use ++i always because somonce mentioned the possibility of a speed increase.

i continued to use it after i learn the compiler will optimise them both to the same machine code ( provided it's not used in expressions )

however, a slight mistype left me with an infinite loop

for( int i = 0 ; i < 10 ; +i )
do_something();

it could happen to you!
Quote:
Quote:Original post by iMalc
Please people, when doing benchmarks you must at least use the values, or the compiler could remove the variable altogether. Or in this case, it could have combined the increments together into one increment. Better to do this:*** Source Snippet Removed ***Though as noted, this test is pointless, as we know they are the same for integers.


He showed you the disassembly output. The incresments WERE there. They were just identical instrucion-wise.
Yeah they were (this time), I did notice that. My comment was supposed to be taken generally, for next time or other cases / people.
Never mind, I directed it poorly.[smile]
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
This is a bit hard for me to read, so bear with me.
#include <stdio.h>class MyVector{  public:      int m_x;    int m_y;    MyVector(int px, int py)    {      m_x = px;      m_y = py;    };    void operator++(int)    {      m_x++;      m_y++;    };    void operator++()    {      ++m_x;      ++m_y;    };    void operator+=( MyVector & param )    {      m_x += param.m_x;      m_y += param.m_y;    };};int main(int argc, char ** argv){  MyVector a(0, 0);  MyVector b(1, 1);  printf("(%i,%i)\n", a.m_x, a.m_y);  a++;  ++a;  a+=b;  printf("(%i,%i)\n", a.m_x, a.m_y);  return 0;}


	.file	"testinc.cpp"	.def	___main;	.scl	2;	.type	32;	.endef	.textLC0:	.ascii "(%i,%i)\12\0"	.align 2.globl _main	.def	_main;	.scl	2;	.type	32;	.endef_main:	pushl	%ebp	movl	%esp, %ebp	subl	$24, %esp	andl	$-16, %esp	movl	$0, %eax	movl	%eax, -20(%ebp)	movl	-20(%ebp), %eax	call	__alloca	call	___main	subl	$4, %esp	pushl	$0	pushl	$0	leal	-8(%ebp), %eax	pushl	%eax	call	__ZN8MyVectorC1Eii	addl	$16, %esp	subl	$4, %esp	pushl	$1	pushl	$1	leal	-16(%ebp), %eax	pushl	%eax	call	__ZN8MyVectorC1Eii	addl	$16, %esp	subl	$4, %esp	pushl	-4(%ebp)	pushl	-8(%ebp)	pushl	$LC0	call	_printf	addl	$16, %esp	subl	$8, %esp	pushl	$0	leal	-8(%ebp), %eax	pushl	%eax	call	__ZN8MyVectorppEi	addl	$16, %esp	subl	$12, %esp	leal	-8(%ebp), %eax	pushl	%eax	call	__ZN8MyVectorppEv	addl	$16, %esp	subl	$8, %esp	leal	-16(%ebp), %eax	pushl	%eax	leal	-8(%ebp), %eax	pushl	%eax	call	__ZN8MyVectorpLERS_	addl	$16, %esp	subl	$4, %esp	pushl	-4(%ebp)	pushl	-8(%ebp)	pushl	$LC0	call	_printf	addl	$16, %esp	movl	$0, %eax	leave	ret	.section	.text$_ZN8MyVectorC1Eii,"x"	.linkonce discard	.align 2.globl __ZN8MyVectorC1Eii	.def	__ZN8MyVectorC1Eii;	.scl	2;	.type	32;	.endef__ZN8MyVectorC1Eii:	pushl	%ebp	movl	%esp, %ebp	movl	8(%ebp), %edx	movl	12(%ebp), %eax	movl	%eax, (%edx)	movl	8(%ebp), %edx	movl	16(%ebp), %eax	movl	%eax, 4(%edx)	popl	%ebp	ret	.section	.text$_ZN8MyVectorppEi,"x"	.linkonce discard	.align 2.globl __ZN8MyVectorppEi	.def	__ZN8MyVectorppEi;	.scl	2;	.type	32;	.endef__ZN8MyVectorppEi:	pushl	%ebp	movl	%esp, %ebp	movl	8(%ebp), %eax	incl	(%eax)	movl	8(%ebp), %eax	incl	4(%eax)	popl	%ebp	ret	.section	.text$_ZN8MyVectorppEv,"x"	.linkonce discard	.align 2.globl __ZN8MyVectorppEv	.def	__ZN8MyVectorppEv;	.scl	2;	.type	32;	.endef__ZN8MyVectorppEv:	pushl	%ebp	movl	%esp, %ebp	movl	8(%ebp), %eax	incl	(%eax)	movl	8(%ebp), %eax	incl	4(%eax)	popl	%ebp	ret	.section	.text$_ZN8MyVectorpLERS_,"x"	.linkonce discard	.align 2.globl __ZN8MyVectorpLERS_	.def	__ZN8MyVectorpLERS_;	.scl	2;	.type	32;	.endef__ZN8MyVectorpLERS_:	pushl	%ebp	movl	%esp, %ebp	movl	8(%ebp), %ecx	movl	8(%ebp), %edx	movl	12(%ebp), %eax	movl	(%eax), %eax	addl	(%edx), %eax	movl	%eax, (%ecx)	movl	8(%ebp), %ecx	movl	8(%ebp), %edx	movl	12(%ebp), %eax	movl	4(%eax), %eax	addl	4(%edx), %eax	movl	%eax, 4(%ecx)	popl	%ebp	ret	.def	__ZN8MyVectorpLERS_;	.scl	3;	.type	32;	.endef	.def	__ZN8MyVectorppEv;	.scl	3;	.type	32;	.endef	.def	__ZN8MyVectorppEi;	.scl	3;	.type	32;	.endef	.def	_printf;	.scl	2;	.type	32;	.endef	.def	__ZN8MyVectorC1Eii;	.scl	3;	.type	32;	.endef


Okay, both __ZN8MyVectorppEi and __ZN8MyVectorppEv, (operator++(int) and operator++()) are identical, as my previous post suggested. ++ isn't defined for floats, so it'd be pointless to try and test them, normal compilers optomize for pre and post increment over integers. I believe __ZN8MyVectorpLERS is operator+=, which had to access another object, so it'd make sense that it would require more opcodes.

_main Is harder to interpret, but it appears as though theres one more opcode between the call to _printf and __ZN8MyVectorppEi, HOWEVER, printf does return the number of characters printed, which I believe is the pushl $0 opcode, since the two prior opcodes are modifying the stack pointer.

So, for gcc compiler (one that appears to be two years out of date actually, I need to fix that), there are no differences between pre and post increment when used without an order of operations context.
william bubel
regarding ++i vs i++ a real speed advantage can be seen when 'i' is an object that has a slow copy constructor. A generic T class implements operator++ in this way:
const T T::operator++(){   T OldObj(*this);   ++*this;   return OldObj;}

so you can see the bootleneck. Standard compilers are allowed to use preincrement instead of postincrement (optimize) only for standard types like int or float.
A thing that is a little strange is that:

int v = 0;
std::cout << v+++v++;

results in undefined behaviour.
[ILTUOMONDOFUTURO]
I would use ++i for any case except *if* I need the value before the increment
afterwards, because

- There may be some overhead in keeping the old value in the user-defined postfix ++ operator, and I just want no need to look at it. If I use prefix, I do not have
to bother.
- Plus, if you consistently use the postfix operator only if you need the value afterwards, your attention to that fact is drawn if you see a ++ postfix expression in the code.
Quote:Original post by rip-off
however, a slight mistype left me with an infinite loop

for( int i = 0 ; i < 10 ; +i )
do_something();

it could happen to you!


[tears]

This topic is closed to new replies.

Advertisement