요즘 만드는것중에서 속도부분에 민감한 코드가 있다.
그래서 나름대로 이것저것 생각을 해서 설계를 하고 있는데 특정 상황에서 플래그 값을 비교해서 더해줘야할 상황이 있고 빼줘야 하는 상황이 있다.
GeSHi © 2004, Nigel McNie
if( POSITIVE )
result = x + y;
else
result = x – y;
Parsed in 0.004 seconds
이부분에서 if문을 사용하면 CPU에서 파이프라이에 있던 명령어들이 모두 날라가서 속도가 느려질태니.. 어떻게든 if문을 안쓸수 있는 방법이 없을까를 고민중…
플래그에 따라서 if문으로 분기해서 빼고 더하고를 결정하지 말고
플래그를 정수로 해서 더해야 하는 플래그는 1로 설정 빼야 하는 플래그는 -1로 설정해서 곱해버리면 어떨까하는 생각이 떠올랐다.
GeSHi © 2004, Nigel McNie
result = x + y*SIGN;
Parsed in 0.003 seconds
오 분기가 없어지니 빨라질것 같다! 라고 생각하며 집에 와서 테스트!!!
GeSHi © 2004, Nigel McNie
#include <windows.h>
#include <iostream>
using namespace std;
#include <time.h>
class SpendTime
{
private:
clock_t s_;
public:
SpendTime()
{
SetTime();
};
~SpendTime() {};
void SetTime()
{
s_ = clock();
};
clock_t GetSpendTime()
{
return clock() – s_;
};
};
int main()
{
SpendTime t;
const int loop = 100;
const int cnt = 1000000;
const int halfcnt = cnt/2;
int posmultor = 1;
int negmultor = -1;
int result = 0;
// if문
t.SetTime();
for( int l=0; l<loop; l++ )
{
for( int i=0; i<cnt; i++ )
{
if( i < halfcnt )
result += i;
else result -= i;
}
result = 0;
}
cout<<t.GetSpendTime()<<endl;
// multi
t.SetTime();
for( int l=0; l<loop; l++ )
{
for( int i=0; i<cnt; i++ )
{
result += i*posmultor;
}
result = 0;
}
cout<<t.GetSpendTime()<<endl; return 0;
}
Parsed in 0.042 seconds
결과는 if문일경우 대략 400ms 곱하기 일경우 700ms정도이다. (디버그 모드에서… 릴리즈 모드는 미칠듯한 최적화로 둘다 0… 이건 좀더 테스트해봐야 할듯…)
아 이런 실망스러운 결과가… 내가 생각하는 것보다 곱하기의 오버해드가 꽤나 심한가 보다.
result = POSITIVE ? x+y : x-y;
시간비교는 해 봐야 겠지만 3항연산자는 분기없이 수행가능합니다.
멋진 실험을 하셨네요 🙂 컴퓨터 아키텍쳐에 관심이 많으신 분인듯하네요.
하지만 요즘은 컴파일러가 워낙 똑똑해져서, 이정도 레벨의 해저드는 우습게 뚫고 나간답니다 (응?)
++i, i++, i=i+1에 대한 실험도 마찬가지죠.
이론상으로는 ++i가 가장 빨라야 하지만, MFC에서 컴파일 후 어셈블리 코드를 들여다보면 셋다 똑같은 명령문으로 치환됩니다.
컴파일하기 전 parsing level에서 variable의 dependency를 검사해서 치환하기도 하고, 컴파일 레벨에서 최적화가 이루어지기도 합니다.
그리고 곱셈은, 컴퓨터 아키텍쳐 교과서에도 그 알고리즘이 나와있지만, 현재로서는 최적화된 알고리즘으로써도 곱셈이 덧셈보다 complexity가 월등히 높습니다.
그리고 조금 더 복잡한 알고리즘에서는, 연산자의 배열에 의한 최적화보다는 time complexity의 order를 줄여 나가는 것이 효과적입니다.
심지어는 nlogn의 알고리즘으로 구현된 자바 어플리케이션이 n²알고리즘의 MFC 어플리케이션보다 월등한 성능을 보여주기도 하니까요 😉
그럼…
겐도님 // 실제로 컴파일러는 if … else와 ? 분기문을 똑같이 처리합니다. ? 분기문이라고 해서 파이프라인 해저드가 없는 것은 아니죠.
다만 컴파일 레벨에서 분기문의 dependency를 분석해서 파이프라인 유실을 최소화하는 것말고는 도리가 없습니다.
이러한 dependency 문제는 비단 pipeline뿐만 아니라, 멀티코어 프로세싱이나 클러스터링에 있어 캐시 정책에도 상당한 위협이 됩니다.
라군님의 고민은 for문 뒤에 pipeline에 적재될 실행명령이 분기에 의해 날아갈 것이고, 이로 인해 속도가 저하되는 것을 우려하는 상황이기 때문에, 아쉽지만 ? 분기문은 어셈블리 레벨에서는 그닥 큰 효과를 보기 힘들다는 것입니다.
개인적으로는 ? 분기를 자주 사용하는 편이긴 하지만, 코드를 혼자만 관리할 것이 아니라 언젠가는 인계를 해야 할 규모의 프로젝트라면 if … else를 쓰는 것이 여러 모로 좋습니다.
겐도 // 삼항연산자도 테스트를 해봤는데 if와 비슷한 결과가 나오더군요. 어쩌면 if를 최적화 시켜서 비슷하게 나오는걸지도 모르겠내요.
風祭 // 확실히 컴파일러에서 여러가지 방법으로 최적화를 해줘서 위와 같은 간단한 코드로는 확실한 비교가 어려울것 같습니다. 적어도 실제와 비슷한 데이터를 집어넣고 비슷한 클래스 구조 까지는 만들어 줘야 하지 않을까 싶습니다.(구조에 따른 오버헤드도 생각하고..) 실제 만들 코드와 비슷하면 비슷할수도 좋겠지만 그건 또 너무 번거롭고 -_-;;;
………..게엔도오?
..msn으로 말 걸면 대답 좀 하슈.