문제
다음 중 어느 것이 더 좋고 그 이유는 무엇입니까? (특히 C ++)
ㅏ.
int i(0), iMax(vec.length());//vec is a container, say std::vector
for(;i < iMax; ++i)
{
//loop body
}
비.
for( int i(0);i < vec.length(); ++i)
{
//loop body
}
길이 기능을 호출하기 때문에 (a)에 대한 조언을 보았습니다. 이것은 나를 괴롭 히고 있습니다. 현대 컴파일러가 (b)의 최적화가 (a)와 비슷하지 않습니까?
해결책
예 (b)는 예제 (a)와 다른 의미를 가지며 컴파일러는 당신이 그것을 쓸 때 그것을 해석해야합니다.
만약 (내가 생각할 수없는 몇 가지 구성된 이유로), 나는 이것을하기 위해 코드를 썼다.
for( int i(0);i < vec.length(); ++i)
{
if(i%4 == 0)
vec.push_back(Widget());
}
다른 결과를 얻을 수 있기 때문에 컴파일러가 Vec.length ()로 각 호출을 최적화하기를 원하지 않았을 것입니다.
다른 팁
좋아요:
for (int i = 0, e = vec.length(); i != e; ++i)
물론 이것은 반복자에게도 효과가 있습니다.
for (vector<int>::const_iterator i = v.begin(), e = v.end(); i != e; ++i)
둘 다 효율적이기 때문에 이것을 좋아합니다 (전화 end()
한 번만), 또한 상대적으로 간결한 (유형이 필요하지 않습니다. vector<int>::const_iterator
한 번).
아무도 명백한 말을하지 않았다는 것에 놀랐습니다.
99.99%의 경우에는 중요하지 않습니다.
계산하는 컨테이너를 사용하지 않는 한 size()
고가의 운영이며, 프로그램이 몇 나노초가 느리게 진행되는 것은 헤아릴 수 없습니다. 코드를 프로필하고 size()
병목 현상입니다.
여기에는 토론해야 할 두 가지 문제가 있습니다.
- 변수 범위
- 최종 조건 재평가
가변 범위
일반적으로 루프 외부에서 루프 변수가 표시되지 않아도됩니다. 그래서 당신이 그것을 내부에서 선언 할 수있는 이유입니다 for
건설하다.
최종 조건 재평가
Andrew Shepherd는 그것을 멋지게 말했습니다. 그것은 최종 조건 내에 기능 호출을 넣는 데 다른 것을 의미합니다.
for( vector<...>::size_type i = 0; i < v.size(); ++i ) { // vector size may grow.
if( ... ) v.push_back( i ); // contrived, but possible
}
// note: this code may be replaced by a std::for_each construct, the previous can't.
for( vector<...>::size_type i = 0, elements = v.size(); i != elements; ++i ) {
}
왜 당신을 몸에 담그고 있습니까? 이 두 가지 대안은 똑같이하는 것을 보지 못합니다. 하나는 고정 된 수의 반복을 수행하는 반면, 다른 하나는 루프 본체에 의존합니다.
또 다른 대안 Colud는
for (vector<T>::iterator it=vec.begin();it!=vec.end();it++){
//loop body
}
루프 외부의 루프 변수가 필요하지 않으면 두 번째 접근 방식이 바람직합니다.
반복자는 실제로 당신에게 좋은 또는 더 나은 성능을 제공합니다. (comp.lang.c ++에는 큰 비교 스레드가있었습니다.
또한 나는 사용할 것입니다
int i = 0;
당신이 사용하고있는 구문과 같은 생성자 대신. 유효하지만 관용적이지 않습니다.
다소 관련이없는 :
경고 : 서명 된 정수와 서명되지 않은 정수의 비교.
배열 및 벡터 지수의 올바른 유형은 다음과 같습니다 size_t.
엄격하게 말하자면, C ++에서는 짝수입니다 std::vector<>::size_type
.
얼마나 많은 C/C ++ 개발자가 여전히 이것을 잘못했는지 놀랍습니다.
생성 된 코드를 보자 (나는 최적화로 MSVS 2008을 사용한다).
ㅏ.
int i(0), iMax(vec.size());//vec is a container, say std::vector
for(;i < iMax; ++i)
{
//loop body
}
For 루프는 2 개의 어셈블러 지침을 생성합니다.
비.
for( int i(0);i < vec.size(); ++i)
{
//loop body
}
For 루프는 8 개의 어셈블러 지침을 생성합니다. Vec.size ()가 성공적으로 상환됩니다.
씨.
for (std::vector<int>::const_iterator i = vec.begin(), e = vec.end(); i != e; ++i)
{
//loop body
}
For 루프는 15 개의 어셈블러 지침을 생성합니다 (모든 것이 감소하지만 코드에는 많은 점프가 있습니다).
따라서 애플리케이션이 성능이 중요하다면 a). 그렇지 않으면 b) 또는 c).
반복자 예제는 다음과 같습니다.
for (vector<T>::iterator it=vec.begin();it!=vec.end();it++){
//loop body
}
루프 반복기 ''루프 본체가 벡터가 재 할당을 유발 해야하는 경우 'IT'를 무효화 할 수 있습니다. 따라서 그것은 동일하지 않습니다
for (int i=0;i<vec.size();++i){
//loop body
}
루프 바디가 Vec에 요소를 추가하는 곳.
간단한 질문 : 수정하고 있습니다 vec
루프에서?
이 질문에 대한 답변은 귀하의 답변으로 이어질 것입니다.
JRH
컴파일러가 들어 오기가 매우 어렵습니다. vec.length()
안전한 지식이 상감되지 않는 한 안전한 지식을 부르십시오. 하지만 적어도 i
두 번째 스타일 "B"로 명확하게 선언해야합니다. length
호출은 루프에서 "수동으로"올라 가야합니다!
이것은 바람직하다 :
typedef vector<int> container; // not really required,
// you could just use vector<int> in for loop
for (container::const_iterator i = v.begin(); i != v.end(); ++i)
{
// do something with (*i)
}
- 벡터가 업데이트되지 않는다고 즉시 알 수 있습니다.
- 누구나 여기서 무슨 일이 일어나고 있는지 알 수 있습니다
- 나는 얼마나 많은 루프를 알고 있습니다
v.end()
마지막 요소를 지나서 포인터를 반환하므로 크기 확인의 오버 헤드가 없습니다.- 다른 컨테이너 또는 값 유형에 대해 쉽게 업데이트 할 수 있습니다
(b) 매번 함수를 계산/호출하지 않습니다.
-발췌 ----
루프 불변 코드 모션 : GCC에는 루프 최적화 제조업체의 일부로 루프 불일치 코드 모션이 포함되어 있으며 부분 중복성 제거 패스에 포함됩니다. 이 최적화는 루프의 수명 내내 변경되지 않는 값을 계산하는 루프에서 지침을 제거합니다.
--- 끝 발췌-
GCC에 대한 최적화 :
https://www.in.redhat.com/software/gnupro/technical/gnupro_gcc.php3
문제를 완전히 회피하지 않겠습니까? boost_foreach
#include <boost/foreach.hpp>
std::vector<double> vec;
//...
BOOST_FOREACH( double &d, vec)
{
std::cout << d;
}