문제

우리는 질문이 있습니다 사이에 성능 차이가 있습니까? i++ 그리고 ++i C에서?

C++에 대한 답은 무엇입니까?

도움이 되었습니까?

해결책

[실행 요약 : ++i를 사용할 특별한 이유가 없다면 i++를 사용하십시오.]

C ++의 경우 답이 좀 더 복잡합니다.

i가 단순한 유형 (C ++ 클래스의 인스턴스가 아님) 인 경우 그런 다음 C에 대한 답변 ( "아니요성능 차이는 없습니다. ") 컴파일러가 코드를 생성하므로 유지됩니다.

그러나 i가 C ++ 클래스의 인스턴스 인 경우 i++++ioperator++ 함수 중 하나를 호출합니다.다음은 이러한 기능의 표준 쌍입니다. 라코 디스

컴파일러가 코드를 생성하지 않고 operator++ 함수를 호출하기 때문에 tmp 변수 및 관련 복사 생성자를 최적화 할 방법이 없습니다.복사 생성자가 비싸면 성능에 상당한 영향을 미칠 수 있습니다.

다른 팁

예. 있습니다.

++ 연산자는 함수로 정의되거나 정의되지 않을 수 있습니다. 기본 유형 (int, double, ...)의 경우 연산자가 내장되어 있으므로 컴파일러가 코드를 최적화 할 수 있습니다. 그러나 ++ 연산자를 정의하는 객체의 경우에는 상황이 다릅니다.

operator ++ (int) 함수는 복사본을 만들어야합니다. 그 이유는 접미사 ++가 보유한 것과 다른 값을 반환 할 것으로 예상되기 때문입니다. 임시 변수에 해당 값을 보유하고 값을 증가시키고 temp를 반환해야합니다. operator ++ (), 접두사 ++의 경우 복사본을 만들 필요가 없습니다. 객체는 자신을 증가시킨 다음 자신을 반환 할 수 있습니다.

다음은 요점에 대한 설명입니다. 라코 디스

operator ++ (int)를 호출 할 때마다 복사본을 만들어야하며 컴파일러는 이에 대해 아무것도 할 수 없습니다. 선택이 주어지면 operator ++ (); 이렇게하면 복사본을 저장하지 않습니다. 많은 증분 (큰 루프?) 및 / 또는 큰 개체의 경우 중요 할 수 있습니다.

다음은 증분 연산자가 다른 번역 단위에있는 경우에 대한 벤치 마크입니다. g ++ 4.5를 사용하는 컴파일러

지금은 스타일 문제 무시 라코 디스 <시간>

O (n) 증가

테스트 라코 디스

결과

가상 머신에서 g ++ 4.5를 사용한 결과 (타이밍은 초 단위) : 라코 디스 <시간>

O (1) 증가

테스트

이제 다음 파일을 가져 오겠습니다. 라코 디스

증가에서 아무것도하지 않습니다. 이것은 증분에 일정한 복잡성이있는 경우를 시뮬레이션합니다.

결과

결과는 이제 매우 다양합니다. 라코 디스 <시간>

결론

성능 측면

이전 값이 필요하지 않은 경우 사전 증가를 사용하는 습관을 만드십시오. 내장 유형과도 일관성을 유지하면 익숙해지며 내장 유형을 사용자 정의 유형으로 대체하더라도 불필요한 성능 손실을 겪을 위험이 없습니다.

의미 적 측면
  • i++increment i, I am interested in the previous value, though를 말합니다.
  • ++iincrement i, I am interested in the current value 또는 increment i, no interest in the previous value를 말합니다. 다시 말하지만, 지금은 아니더라도 익숙해 질 것입니다.

    크 누스.

    조기 최적화는 모든 악의 근원입니다. 너무 이른 비관.

컴파일러가 접미사의 경우 임시 변수 사본을 최적화 할 수 없다고 말하는 것은 전적으로 옳지 않습니다.VC를 사용한 빠른 테스트는 적어도 특정 경우에 그렇게 할 수 있음을 보여줍니다.

다음 예에서 생성 된 코드는 예를 들어 접두사와 접미사에 대해 동일합니다. 라코 디스

++ testFoo 또는 testFoo ++를 수행하더라도 동일한 결과 코드를 얻을 수 있습니다.사실, 사용자로부터 카운트를 읽지 않고 옵티마이 저는 모든 것을 상수로 낮추었습니다.그래서 이것은 : 라코 디스

결과는 다음과 같습니다. 라코 디스

따라서 postfix 버전이 더 느릴 수있는 경우는 분명하지만, 사용하지 않는 경우 최적화 프로그램이 임시 사본을 제거하기에 충분할 수 있습니다.

Google C ++ 스타일 가이드 에는 : <인용구>

사전 증가 및 사전 감소

증가 및 감소 연산자의 접두사 형식 (++ i)을 반복자 및 기타 템플릿 개체

정의 : 변수가 증가 (++ i 또는 i ++) 또는 감소 (--i 또는 i--) 식의 값이 사용되지 않고 결정해야합니다. 사전 증가 (감소) 또는 사후 증가 (감소) 여부.

장점 : 반환 값이 무시되면 "pre"형식 (++ i)은 결코 작지 않습니다. "post"형식 (i ++)보다 효율적이며 종종 더 효율적입니다. 이는 사후 증가 (또는 감소)에 i의 사본이 필요하기 때문입니다. 표현의 가치입니다. 내가 반복자이거나 다른 비 스칼라 유형은 복사 비용이 많이들 수 있습니다. 둘 이후 값이 무시되면 증분 유형이 동일하게 작동합니다. 항상 사전 증가?

단점 : C에서 발전된 전통은 특히 for 루프에서는 표현식 값이 사용되지 않습니다. 일부 찾기 "제목"(i)이 앞에 있기 때문에 사후 증가는 읽기가 더 쉽습니다. 영어와 마찬가지로 "동사"(++)

결정 : 단순 스칼라 (객체가 아닌) 값의 경우 선호 할 이유가 없습니다. 양식 및 우리는 둘 중 하나를 허용합니다. 반복기 및 기타 템플릿 유형의 경우 사전 증분.

최근에 코드 토크에 Andrew Koenig의 훌륭한 게시물을 지적하고 싶습니다.

http://dobbscodetalk.com/index.php? option= com_myblog & show= Efficiency-versus-intent.html & Itemid= 29

우리 회사에서도 일관성과 성능을 위해 ++ iter 규칙을 사용합니다.그러나 Andrew는 의도 대 성능에 대해 간과 된 세부 사항을 제기합니다.++ iter 대신 iter ++를 사용하고 싶을 때가 있습니다.

따라서 먼저 의도를 결정하고 사전 또는 사후가 중요하지 않은 경우 추가 개체 생성을 피하고 던짐으로써 성능상의 이점이 있으므로 사전으로 이동하십시오.

@Ketan <인용구>

... 의도 대 성능과 관련하여 간과 된 세부 정보를 제공합니다.++ iter 대신 iter ++를 사용하고 싶을 때가 있습니다.

분명히 post와 pre-increment는 서로 다른 의미를 가지고 있으며 결과가 사용될 때 적절한 연산자를 사용해야한다는 데 모든 사람들이 동의한다고 확신합니다.나는 결과가 버려 질 때 (for 루프에서와 같이) 무엇을해야하는지에 대한 질문이라고 생각합니다. 질문 (IMHO)에 대한 대답은 성능 고려 사항이 기껏해야 무시할 수 있으므로 더 자연스러운 작업을 수행해야한다는 것입니다.제 자신에게는 ++i가 더 자연 스럽지만 제 경험에 따르면 제가 소수이고 i++를 사용하면 코드를 읽는 대부분 사람들에게 금속 오버 헤드가 줄어 듭니다.

이것이 언어를 " ++C "라고 부르지 않는 이유입니다. [*]

[*] ++C가 더 논리적 인 이름에 대한 의무적 인 토론을 삽입하십시오.

Mark : operator ++가 인라인되기에 좋은 후보라는 점을 지적하고 싶었습니다. 컴파일러가 그렇게하기로 결정하면 대부분의 경우 중복 된 복사본이 제거됩니다.(예 : 반복자가 일반적으로 사용하는 POD 유형)

즉, 대부분의 경우 ++ iter를 사용하는 것이 여전히 더 나은 스타일입니다.:-)

간의 성능 차이 ++i 그리고 i++ 연산자를 값을 반환하는 함수로 생각하고 구현 방법을 보면 더 명확해질 것입니다.무슨 일이 일어나고 있는지 더 쉽게 이해할 수 있도록 다음 코드 예제에서는 다음을 사용합니다. int 마치 그것이 struct.

++i 변수를 증가시키고, 그 다음에 결과를 반환합니다.이 작업은 최소한의 CPU 시간으로 내부에서 수행할 수 있으며 대부분의 경우 한 줄의 코드만 필요합니다.

int& int::operator++() { 
     return *this += 1;
}

그러나 같은 말은 할 수 없다 i++.

사후 증분, i++, 원래 값을 반환하는 것으로 간주되는 경우가 많습니다. ~ 전에 증가.하지만, 함수는 완료되었을 때만 결과를 반환할 수 있습니다..결과적으로 원래 값이 포함된 변수의 복사본을 만들고 변수를 증가시킨 다음 원래 값이 포함된 복사본을 반환해야 합니다.

int int::operator++(int& _Val) {
    int _Original = _Val;
    _Val += 1;
    return _Original;
}

사전 증가와 사후 증가 사이에 기능적 차이가 없는 경우 컴파일러는 둘 사이에 성능 차이가 없도록 최적화를 수행할 수 있습니다.그러나 다음과 같은 복합 데이터 유형의 경우 struct 또는 class 관련된 경우 복사 생성자는 사후 증분 시 호출되며 전체 복사가 필요한 경우 이 최적화를 수행할 수 없습니다.따라서 사전 증분은 일반적으로 사후 증분보다 더 빠르고 메모리도 덜 필요합니다.

  1. ++ i -반환 값 사용하지 않음 더 빠름
  2. i ++ -반환 값을 사용하여 더 빠르게

    반환 값을 사용하지 않을 때 컴파일러는 ++ i 의 경우 임시를 사용하지 않도록 보장합니다.더 빠르다는 보장은 없지만 더 느리지는 않습니다.

    반환 값 i ++ 사용 하면 프로세서가 증분과 왼쪽은 서로 의존하지 않기 때문에 파이프 라인에 넣습니다.++ i는 사전 증가 작업이 끝까지 구불 구불 할 때까지 프로세서가 왼쪽을 시작할 수 없기 때문에 파이프 라인을 멈출 수 있습니다.다시 말하지만, 프로세서가 다른 유용한 정보를 찾을 수 있기 때문에 파이프 라인 중단이 보장되지 않습니다.

성능 이점이없는 내장형에서도 ++ i를 사용해야하는 이유는 스스로 좋은 습관을 만들기 위해서입니다.

@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)

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.

@wilhelmtell

The compiler can elide the temporary. Verbatim from the other thread:

The C++ compiler is allowed to eliminate stack based temporaries even if doing so changes program behavior. MSDN link for VC 8:

http://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx

Time to provide folks with gems of wisdom ;) - there is simple trick to make C++ postfix increment behave pretty much the same as prefix increment (Invented this for myself, but the saw it as well in other people code, so I'm not alone).

Basically, trick is to use helper class to postpone increment after the return, and RAII comes to rescue

#include <iostream>

class Data {
    private: class DataIncrementer {
        private: Data& _dref;

        public: DataIncrementer(Data& d) : _dref(d) {}

        public: ~DataIncrementer() {
            ++_dref;
        }
    };

    private: int _data;

    public: Data() : _data{0} {}

    public: Data(int d) : _data{d} {}

    public: Data(const Data& d) : _data{ d._data } {}

    public: Data& operator=(const Data& d) {
        _data = d._data;
        return *this;
    }

    public: ~Data() {}

    public: Data& operator++() { // prefix
        ++_data;
        return *this;
    }

    public: Data operator++(int) { // postfix
        DataIncrementer t(*this);
        return *this;
    }

    public: operator int() {
        return _data;
    }
};

int
main() {
    Data d(1);

    std::cout <<   d << '\n';
    std::cout << ++d << '\n';
    std::cout <<   d++ << '\n';
    std::cout << d << '\n';

    return 0;
}

Invented is for some heavy custom iterators code, and it cuts down run-time. Cost of prefix vs postfix is one reference now, and if this is custom operator doing heavy moving around, prefix and postfix yielded the same run-time for me.

Both are as fast ;) If you want it is the same calculation for the processor, it's just the order in which it is done that differ.

For example, the following code :

#include <stdio.h>

int main()
{
    int a = 0;
    a++;
    int b = 0;
    ++b;
    return 0;
}

Produce the following assembly :

 0x0000000100000f24 <main+0>: push   %rbp
 0x0000000100000f25 <main+1>: mov    %rsp,%rbp
 0x0000000100000f28 <main+4>: movl   $0x0,-0x4(%rbp)
 0x0000000100000f2f <main+11>:    incl   -0x4(%rbp)
 0x0000000100000f32 <main+14>:    movl   $0x0,-0x8(%rbp)
 0x0000000100000f39 <main+21>:    incl   -0x8(%rbp)
 0x0000000100000f3c <main+24>:    mov    $0x0,%eax
 0x0000000100000f41 <main+29>:    leaveq 
 0x0000000100000f42 <main+30>:    retq

You see that for a++ and b++ it's an incl mnemonic, so it's the same operation ;)

When you write i++ you are telling the compiler to increment after it finishes this line or loop.

++i is a little different than i++. In i++ you increment after you finish the loop but ++i you increment directly before the loop finishes.

++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;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top