성능 차이가 있습니까? i++
++i
결과 값을 사용하지 않을 경우 와 있습니까?
답변
행정상 개요 : 아니다.
i++
++i
의 이전 값 때문에 잠재적으로 느릴 수 있습니다.i
나중에 사용하기 위해 저장해야 할 수도 있지만 실제로는 모든 최신 컴파일러가이를 최적화합니다.
우리는이 기능의 코드를 보면이 설명과 함께 모두 수 ++i
와 i++
.
$ cat i++.c
extern void g(int i);
void f()
{
int i;
for (i = 0; i < 100; i++)
g(i);
}
파일은 제외하고, 동일 ++i
및 i++
:
$ diff i++.c ++i.c
6c6
< for (i = 0; i < 100; i++)
---
> for (i = 0; i < 100; ++i)
컴파일하고 생성 된 어셈블러를 얻습니다.
$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c
그리고 생성 된 객체와 어셈블러 파일이 동일하다는 것을 알 수 있습니다.
$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e
$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22
답변
에서 의도 대비 효율성 앤드류 코에 닉의 :
첫째, 적어도 정수 변수가 관련된 경우
++i
보다 더 효율적인 것은 분명하지i++
않습니다.
그리고 :
따라서 두 가지 작업 중 어느 것이 더 빠르지 않은지, 두 가지 작업 중 어떤 것이 달성하려는 것을 더 정확하게 표현하는지에 대한 질문이 있습니다. 변수 값을 복사하고 변수를 증가시킨 다음 복사본을 버릴 이유가 없기 때문에 표현식 값을 사용하지 않는 경우
i++
대신 대신 사용할 이유++i
가 없다고 제출합니다.
따라서 결과 값을 사용하지 않으면을 사용합니다 ++i
. 그러나 그것이 더 효율적이기 때문에가 아닙니다 : 그것은 내 의도를 올바르게 진술하기 때문입니다.
답변
더 나은 대답은 ++i
때로는 빠르지 만 결코 느리지 않을 것입니다.
모두가 그것을 가정하고있는 것 같습니다 i
가 같은 기본 제공 유형int
. 이 경우 측정 가능한 차이가 없습니다.
그러나 i
복잡한 유형이라면 측정 가능한 차이를 찾을 수 있습니다. 들어 i++
당신을 증가하기 전에 클래스의 사본을해야합니다. 사본에 포함 된 내용에 따라 ++it
최종 값을 반환 할 수 있기 때문에 실제로 느려질 수 있습니다.
Foo Foo::operator++()
{
Foo oldFoo = *this; // copy existing value - could be slow
// yadda yadda, do increment
return oldFoo;
}
또 다른 차이점은 ++i
값 대신 참조를 반환하는 옵션 것입니다. 다시 말하지만, 객체의 사본을 만드는 데 관련된 내용에 따라 속도가 느려질 수 있습니다.
이것이 발생할 수있는 실제 예는 반복자를 사용하는 것입니다. 반복자를 복사하면 응용 프로그램에서 병목 수 없을 수도 있습니다,하지만 여전히 사용하는 습관을 좋은 습관이다 ++i
대신에 i++
결과가 영향을받지된다.
답변
짧은 답변:
속도 i++
와 ++i
는 차이가 없습니다 . 좋은 컴파일러는 두 경우에 다른 코드를 생성해서는 안됩니다.
긴 대답 :
다른 모든 대답에서 언급하지 못한 것은 발견 된 표현 내에서만 ++i
대 차이 i++
가 의미가 있다는 것입니다.
의 경우 for(i=0; i<n; i++)
는은 i++
자신의 표현에 혼자 : 전과 시퀀스 포인트가 i++
그것을 하나씩있다. 따라서 생성 된 유일한 기계 코드는 “증가한 i
로는 1
“이 프로그램의 나머지 부분과 관련하여 염기 서열을 얼마나 잘 정의되어있다. 그래서 당신은 접두사로 변경한다면 ++
, 그것은 문제가 조금, 당신은 여전히 머신 코드 “증가 얻을 것없는 것 i
에 의해를 1
“.
사이의 차이 ++i
와 i++
같은 표현 만 문제 array[i++] = x;
대 array[++i] = x;
. 어떤 사람들은 그 위치 i
에있는 레지스터를 나중에 다시로드해야하기 때문에 이러한 작업에서 접미사가 느려질 것이라고 주장하고 말할 수 있습니다. 그러나 C 표준이 호출 할 때 “추상 기계의 동작을 중단하지”않는 한 컴파일러는 원하는 방식으로 명령을 자유롭게 주문할 수 있습니다.
따라서 다음 array[i++] = x;
과 같이 기계 코드로 변환 된다고 가정 할 수 있습니다 .
i
레지스터 A에 값을 저장합니다 .- 레지스터 B에 어레이의 주소를 저장하십시오.
- A와 B를 추가하고 결과를 A에 저장하십시오.
- A로 표시되는이 새 주소에 x 값을 저장하십시오.
- 저장 가치
i
// 레지스터 A의 // 비효율적으로 저장하기 때문에 이미 한 번 수행했습니다. - 증분 레지스터 A.
- 에 레지스터 A를 저장하십시오
i
.
컴파일러는 다음과 같이 코드를보다 효율적으로 생성 할 수 있습니다.
i
레지스터 A에 값을 저장합니다 .- 레지스터 B에 어레이의 주소를 저장하십시오.
- A와 B를 더하고 결과를 B에 저장하십시오.
- 증분 레지스터 A.
- 에 레지스터 A를 저장하십시오
i
. - … // 나머지 코드.
C 프로그래머로서 postfix ++
가 마지막에 발생 한다고 생각하도록 훈련을 받았다고 해서 기계 코드를 그런 식으로 주문할 필요는 없습니다.
따라서 ++
C 에서는 접두사와 접미사 사이에 차이가 없습니다 . 이제 C 프로그래머는 다양해야합니다. 이유에 관계없이 접두사를 일관성없이 사용하고 어떤 경우에는 접두사를 사용하는 사람들이 있습니다. 이는 C의 작동 방식에 대해 확신이 없거나 언어에 대한 지식이 잘못되었음을 나타냅니다. 이것은 항상 나쁜 징조이며, 결과적으로 그들이 미신이나 “종교적 교리”에 근거하여 프로그램에서 다른 의심스러운 결정을 내린다고 제안합니다.
“접두사 ++
가 항상 빠릅니다”는 C 프로그래머들 사이에서 흔히 볼 수있는 잘못된 교리 중 하나입니다.
답변
Scott Meyers에서 보다 효과적인 c ++ 항목 6 : 접두사와 접미사 형태의 증분 및 감소 연산 구분 .
접두사 버전은 객체, 특히 반복자와 관련하여 항상 접미사보다 우선합니다.
연산자의 호출 패턴을 보면이 이유가 있습니다.
// Prefix
Integer& Integer::operator++()
{
*this += 1;
return *this;
}
// Postfix
const Integer Integer::operator++(int)
{
Integer oldValue = *this;
++(*this);
return oldValue;
}
이 예제를 보면 접두사 연산자가 항상 접미사보다 어떻게 효율적인지 쉽게 알 수 있습니다. 접미사를 사용할 때 임시 개체가 필요하기 때문입니다.
이것이 반복자를 사용하는 예제를 볼 때 항상 접두사 버전을 사용하는 이유입니다.
그러나 int가 지적한 것처럼 컴파일러 최적화로 인해 실제로 차이가 없습니다.
답변
마이크로 최적화가 걱정되는 경우 추가 관찰 사항이 있습니다. 다음과 같은 경우 감소 루프는 증가하는 루프 (명령 세트 아키텍처에 따라 ARM과 같은) 증가 루프보다 ‘효율적’일 수 있습니다.
for (i = 0; i < 100; i++)
각 루프마다 다음에 대한 명령이 하나씩 있습니다.
- 추가
1
에i
. i
보다 작은 지 비교하십시오100
.- 조건 분기는 경우
i
보다는 더 적은이다100
.
감소 루프 반면 :
for (i = 100; i != 0; i--)
루프에는 다음에 대한 지침이 있습니다.
- 감소
i
, CPU 레지스터 상태 플래그 설정 - CPU 레지스터 상태 (
Z==0
) 에 따른 조건부 분기 입니다.
물론 이것은 0으로 감소 할 때만 작동합니다!
ARM 시스템 개발자 안내서에서 기억합니다.
답변
“어느 쪽이 빠를까”라는 질문이 어떤 결정을 내릴 것인지 결정하지 마십시오. 그다지 신경 쓰지 않을 것입니다. 게다가 프로그래머의 읽기 시간이 기계 시간보다 훨씬 비쌉니다.
사람이 코드를 읽는 데 가장 적합한 것을 사용하십시오.