E D R , A S I H C RSS

i++VS++i

C/C++ 에서 ++i 와 i++ 의 성능 차이에 대한 비교

  • 사용한 컴파일러 : Microsoft 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86 (Microsoft Visual C++ 6.0 에 Service Pack 5 를 설치했을때의 컴파일러)




1. 그냥 사용

  • 비교한 C/C++ 소스

~cpp 
#include <stdio.h>
void main()
{
	int i;
	scanf("%d", &i);
	++i;
	printf("%d", i);
}
~cpp 
#include <stdio.h>
void main()
{
	int i;
	scanf("%d", &i);
	i++;    // 이렇게 하면 차이가 당연히 없지 않을까요? 이럴때는 선이든 후이든 증가한뒤에 printf에서 그 변수를 사용했으니..
	printf("%d", i);     // 문제가 되는건 함수(i++)또는 함수(++i)이런데에서 문제가 생길거 같은데..
}
간단히 생각하면 되는 것이었습니다.. i++ 이나 ++i 모두 값(value)을 생성하는 연산자 입니다. 그러므로.. i 가 5 일때 i++ 의 값은 5이므로.. printf("%d", i) 는 5를 찍어주겠지요.

1.1. 최적화하지 않을때

  • ++i 의 컴파일된 어셈블리 소스
    ~cpp 
    	mov	ecx, DWORD PTR _i$[ebp]	; 변수 i 인 _i$[ebp] 를 ecx 로 옮기고
    	add	ecx, 1			; ecx 에 1 을 더하고
    	mov	DWORD PTR _i$[ebp], ecx	; ecx 를 다시 _i$[ebp] 로
    

  • i++ 의 컴파일된 어셈블리 소스
    ~cpp 
    	mov	ecx, DWORD PTR _i$[ebp]	; 차이 없음
    	add	ecx, 1
    	mov	DWORD PTR _i$[ebp], ecx
    

1.2. 크기 최적화

  • ++i 의 컴파일된 어셈블리 소스
    ~cpp 
    	inc	DWORD PTR _i$[ebp]		; 변수 i 인 _i$[ebp] 를 1 만큼 증가시킴
    

  • i++ 의 컴파일된 어셈블리 소스
    ~cpp 
    	inc	DWORD PTR _i$[ebp]		; 차이 없음
    

1.3. 속도 최적화

  • ++i 의 컴파일된 어셈블리 소스
    ~cpp 
    	mov	eax, DWORD PTR _i$[esp+12]	; 변수 i 인 _i$[esp+12] 를 eax 로 옮기고
    	inc	eax			; eax 를 1 만큼 증가시킴
    

  • i++ 의 컴파일된 어셈블리 소스
    ~cpp 
    	mov	eax, DWORD PTR _i$[esp+12]	; 차이 없음
    	inc	eax
    


2. for 문에서 사용

  • 비교한 C/C++ 소스

~cpp 
#include <stdio.h>
void main()
{
	for(int i = 0 ; i < 10 ; ++i)
		printf("%d", i);
}
~cpp 
#include <stdio.h>
void main()
{
	for(int i = 0 ; i < 10 ; i++)
		printf("%d", i);
}

2.1. 최적화하지 않을때

  • ++i 의 컴파일된 어셈블리 소스
    ~cpp 
    	mov	eax, DWORD PTR _i$[ebp]	; 변수 i 인 _i$[ebp] 를 eax 로 옮기고
    	add	eax, 1			; eax 에 1 을 더하고
    	mov	DWORD PTR _i$[ebp], eax	; eax 를 다시 _i$[ebp] 로
    

  • i++ 의 컴파일된 어셈블리 소스
    ~cpp 
    	mov	eax, DWORD PTR _i$[ebp]	; 차이 없음
    	add	eax, 1
    	mov	DWORD PTR _i$[ebp], eax
    

2.2. 크기 최적화

  • ++i 의 컴파일된 어셈블리 소스
    ~cpp 
    	inc	esi			; 변수 i 인 esi 를 1 만큼 증가시킴
    

  • i++ 의 컴파일된 어셈블리 소스
    ~cpp 
    	inc	esi			; 차이 없음
    

2.3. 속도 최적화

  • ++i 의 컴파일된 어셈블리 소스
    ~cpp 
    	inc	esi			; 변수 i 인 esi 를 1 만큼 증가시킴
    

  • i++ 의 컴파일된 어셈블리 소스
    ~cpp 
    	inc	esi			; 차이 없음
    


3. 함수 에서 사용

  • 비교한 C/C++ 소스

~cpp 
#include <stdio.h>
void main()
{
	int i;
	scanf("%d", &i);
	printf("%d", ++i);
}
~cpp 
#include <stdio.h>
void main()
{
	int i;
	scanf("%d", &i);
	printf("%d", i++);
}

3.1. No 최적화

  • ++i 의 컴파일된 어셈블리 소스
    ~cpp 
    	mov	ecx, DWORD PTR _i$[ebp]
    	add	ecx, 1
    	mov	DWORD PTR _i$[ebp], ecx
    	mov	edx, DWORD PTR _i$[ebp]
    	push	edx
    	push	OFFSET FLAT:$SG528
    	call	_printf
    

  • i++ 의 컴파일된 어셈블리 소스
    ~cpp 
    	mov	ecx, DWORD PTR _i$[ebp]
    	mov	DWORD PTR -8+[ebp], ecx
    	mov	edx, DWORD PTR -8+[ebp]
    	push	edx
    	push	OFFSET FLAT:$SG528
    	mov	eax, DWORD PTR _i$[ebp]
    	add	eax, 1
    	mov	DWORD PTR _i$[ebp], eax
    	call	_printf
    

3.2. 크기 최적화

  • ++i 의 컴파일된 어셈블리 소스
    ~cpp 
    	inc	DWORD PTR _i$[ebp]
    	push	DWORD PTR _i$[ebp]
    	push	esi
    	call	_printf
    

  • i++ 의 컴파일된 어셈블리 소스
    ~cpp 
    	mov	eax, DWORD PTR _i$[ebp]
    	inc	DWORD PTR _i$[ebp]
    	push	eax
    	push	esi
    	call	_printf
    

3.3. 속도 최적화

  • ++i 의 컴파일된 어셈블리 소스
    ~cpp 
    	mov	eax, DWORD PTR _i$[esp+12]
    	inc	eax
    	push	eax
    	push	OFFSET FLAT:??_C@_02MECO@?$CFd?$AA@
    	mov	DWORD PTR _i$[esp+20], eax
    	call	_printf
    

  • i++ 의 컴파일된 어셈블리 소스
    ~cpp 
    	mov	eax, DWORD PTR _i$[esp+12]
    	mov	ecx, eax
    	inc	eax
    	push	ecx
    	push	OFFSET FLAT:??_C@_02MECO@?$CFd?$AA@
    	mov	DWORD PTR _i$[esp+20], eax
    	call	_printf
    

4. 연산자 재정의로 구현했을때

class 에서 operator overloading 으로 전위증가(감소)와 후위증가(감소)는 다음과 같이 구현되어 있다.
~cpp 
MyInteger& MyInteger::operator++() // 전위증가
{
 *this += 1;
 return *this;
}
const MyInteger MyInteger::operator++(int) // 후위증가. 전달인자로 int 가 있지만
                                         // 컴파일러는 내부적으로 operator++(0)을 호출한다.
{
 const MyInteger oldValue = *this;
 ++(*this);

 return oldValue;
}
 
연산자 재정의를 하여 특정 개체에 대해 전위증가와 후위증가를 사용할 때에는 전위증가가 후위증가보다 효율이 좋다. operator++(int) 함수에서는 임시 객체를 생성하는 부분이 있다.
- from MoreEffectiveC++

4.1. STL 에서

++i, 나 i++ 둘다 상관 없는 상황이라면, ++i에 습관을 들이자, 위의 연산자 재정의는 STL을 사용한다면 일반적인 경우이다. 후위 연산자가 구현된 Iterator는 모두 객체를 복사하는 과정을 거친다. 컴파일러단에서 Iterator 의 복사를 최적화 할수 있는 가능성에서는 보장할 수 없다. 따라서, 다음과 같은 경우
~cpp 
static const int MAX = 5;
int array[5] = {1,2,3,4,5}
vector<int> intArray(&array[0], &array[MAX]);

for(vector<int>::iterator i = intArray.begin(); i != intArray.end(); ++i){
    cout << *i << endl;
}
가 객체 복사를 하지 않는다. 객체가 크다면 이 비용은 무시할 수 없다. 더 궁금하면, STL 소스를 분석해 보자.
--NeoCoin

5. 결론

그냥 사용한 경우나, for 문에서 사용한 경우는 ++i 와 i++ 의 성능 차이가 없다. 그러나 함수의 전달인자로 사용한 경우는 ++i 보다 i++ 의 코드가 명령어 한개 정도 길어진다. 하지만 그냥 사용한 경우나 for 문에서 사용한 경우에는 i++ 을 쓴 곳을 ++i 로 서로 바꿔 써도 상관 없으나, 함수의 전달인자로 사용한 경우에는 i++ 을 쓴 곳을 ++i 로 바꾸면 실 결과가 달라진다. 그러므로 함수에서 i++ 을 사용하고 있을 경우 프로그램이 한 줄 이라도 추가되지 않고 ++i 로 바꿀수 있으면 바꾸는 것이 더 효율적이다. 또한 그냥 사용할 경우나, for 문에서 사용한 경우는 ++i 를 쓰지 않아도 상관 없다. --상규

6. 토론

  • 아악... 어셈이다. 전 봐도 모르겠으니 걍 제가 본 글귀를 그대로 인용하겠습니다.

~cpp 
여기에서 교과서적인 이야기를 하나 하고 넘어가야 할 것 같다. 루프 안에서 항상 선 증가를 사용하는 것이 좋은 이유는 무엇일까?

효율성 때문이라는 것이 정답이다. 후 증가 연산자는 변수의 이전 값을 돌려 주므로 이전 값을 담을 임시적인 변수를 만들고 파괴하는 과정이 일어나게 된다. 
후 증가로도 선 증가와 동일한 방식의 루프를 만드는 것이 가능하지만, 후 증가를 사용할 특별한 이유가 없다면 항상 선 증가 또는 선 감소 연산자를 
사용하는 것이 바람직히다.
요즘 컴파일러들은 최적화가 잘 되어서 이전 값을 돌려주기위해 이전 값을 담을 임시 변수를 만들고 값을 증가시킨 후 임시 변수에 있는 이전 값을 돌려주고 임시 변수를 파괴하는 방식으로 하지 않고, 이전 값을 먼저 돌려주고 값을 증가시킵니다.

속도가 아주 빨라야 하는 프로그래밍을 할 때는 특정 컴파일러에서 어떻게 할 때가 성능이 더 좋은지 알 필요가 있을수도 있다.
물론 특정 컴파일러라는 것이 언제나 명시되어야 한다. 일반화는 일반적으로 옳지 않다.

쩝.. 저는 별로 신경쓰지 않는데요... (무감각하다는;;;) Amdahl's Law 였나.. 프로그램 속도를 증가시키려면, 제일 시간을 많이 잡아먹는 부분을 수정하라고... 쩝.. 마이크로 프로그램이나. 리얼타임 어플리케이션같은곳에서는 필요할수도 있겠군요.; - 임인택

7. ++i 찬성

동일한 기능에 쓴다면, ++i 에 습관을 들이는 것을 추천한다. STL같은 연산자 재정의 라이브러리 때문 --NeoCoin

8. i++ 찬성

i++은 그 특유의 기능이 필요할 때만 쓰는것이 좋을것 같다. i++를 쓰면 다음과 같이 두줄이 한줄로 주는 경우가 있기 때문이다.

현재 자료를 찍고, 다음 자료로 카운팅을 하려 한다.

~cpp 
...
cout << data[i] << endl
i += 1;
...
가 이렇게 한줄로 주는 후위 연산자의 기능을 이용할때 쓰자.

~cpp 
cout << data[i++] << endl;
Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:31:42
Processing time 0.0468 sec