본문 바로가기

Visual Studio

이상한 최적화 코드 (Visual studio optimization)

이상한 최적화 코드 (Visual studio optimization)

http://somma.egloos.com/2876579

어제 다섯시간가까이 이 문제 때문에 삽질을 했습니다. >.<
디버그 모드에서는 분명히 아무 문제 없는 코드였는데 릴리즈 모드에서는 문제가 발생하더군요.

아래는 그 괴상 망측한 최적화 코드 문제가 발생한 클래스 입니다.
객체 메소드로 Thread procedure 를 사용하기 위해 만든 스레드 래퍼 클래스입니다. (델파이의 TThread 를 흉내냈죠)


class TThread
{
public:
TThread()
: m_ThreadHandle(NULL),
m_ThreadId(0),
m_Suspended(false),
m_Terminated(true),
m_ActiveState(false)
{};

virtual ~TThread(){};

int Start(void);
int Terminate(void);
bool IsTerminated() {return m_Terminated;};

bool getActiveState() { return m_ActiveState; };
void setActiveState(bool Value) { m_ActiveState = Value; };
protected:
virtual void RealThreadProc(void) = 0; // 스레드 로직 구현
private:
HANDLE m_ThreadHandle;
unsigned int m_ThreadId;
bool m_Suspended;
bool m_Terminated;
bool m_ActiveState;

// Thread function
static unsigned __stdcall _ThreadProc(LPVOID lpParameter);
};



/** ---------------------------------------------------------------------------
\brief

\param
\return
\code
\endcode
-----------------------------------------------------------------------------*/
int TThread::Terminate(void)
{
if (true == m_Terminated)
{
return 0;
}

m_Terminated = true; // RealThreadProc() 루프를 종료시킨다.
while(1)
{
if (true != getActiveState())
break;
/*else
Sleep(0);*/
}


return 0;
}



이 클래스를 이용하기 위해서 TThread 클래스를 상속받아 RealThreadProc() 메소드를 구현합니다.

class TThreadTest: public TThread
{
public:
TThreadTest(){};
virtual ~TThreadTest(){};
protected:
virtual void RealThreadProc(void);
private:
};





/** ---------------------------------------------------------------------------
\brief

\param
\return
\code
\endcode
-----------------------------------------------------------------------------*/
/*virtual*/ void TThreadTest::RealThreadProc(void)
{
while (false == IsTerminated())
{
printf("this is %s\n", __FUNCTION__);
}

// must change flag!!
//
setActiveState(false);
}


TThread::Terminate() 를 호출하면 m_Terminate 변수가 바뀌고, RealThreadProc() 내의 루프가 종료되면서 setActiveState() 를 통해서 m_ActiveState 변수를 false 로 바꿔주죠..
TThread::Termiante() 메소드는 getActiveState() 를 통해 루프를 돌면서 m_ActiveState 가 false 가 되길 기다리죠.

디버그 모드에서는 잘 동작합니다. 로직상으로도 문제될 소지가 없어 보입니다. 사실 별 문제 없구요.
그러나 디폴트로 최적화 옵션 (크기 최소화 /O1, 속도 최대화 /O2 옵션)이 활성화 되는 릴리즈 모드에서는 문제가 발생하네요.
TThread::Terminate() 메소드 내의 while() 루프가 무한루프에 빠져버립니다. 원인을 찾지 못하다가 최적화 문제일거 같은 생각이 들어 확인해 봤더니 그런거 같더군요.

[VS .NET 2003 크기 최소화 (/O1)]
while() 루프 내의 코드를 살펴보면 무한루프에 빠질 수 밖에 없는 코드가 만들어졌습니다.
컴파일러는 나름대로 최적화를 하기 위해 저런 코드를 만든거 같은데 이해할 수 없군요 :-(

00401023 C6 41 0D 01 mov byte ptr [ecx+0Dh],1
00401027 8A 49 0E mov cl,byte ptr [ecx+0Eh]
while(1)
{
if (true != getActiveState())
0040102A 80 F9 01 cmp cl,1
0040102D 74 FB je TThread::Terminate+0Dh (40102Ah)
break;
}

[VS .NET 2003 속도 최대화 (/O2)]
이번엔 속도 최대화 옵션을 사용해서 생성된 코드 입니다.
속도 최대화 옵션을 하니 TThread::Termiante() 메소드를 inlining 해 버리는 군요.
맨 아래 두 라인이 while() 루프인데 역시나 마찬가지로 무한루프에 빠질 수 밖에 없는 코드를 만들어 버리네요. >.<


tt.Terminate();
00401124 80 7C 24 19 01 cmp byte ptr [esp+19h],1
00401129 74 0D je main+98h (401138h)
0040112B 8A 44 24 1A mov al,byte ptr [esp+1Ah]
0040112F C6 44 24 19 01 mov byte ptr [esp+19h],1
00401134 3C 01 cmp al,1
00401136 74 FC je main+94h (401134h)



[VS 2005 속도 최대화 (/O2)]
VS 2005 에서는 어떨까 하고.. VC++ 2005 express 를 깔아서 테스트 해봤습니다. (제가 좀 집요한 면이 있어요 ^^)
역시나 무한루프 !!

tt.Terminate();
004010FA cmp byte ptr [esp+1Dh],1
004010FF je wmain+0A4h (401114h)
00401101 mov al,byte ptr [esp+1Eh]
00401105 mov byte ptr [esp+1Dh],1
0040110A lea ebx,[ebx]
00401110 cmp al,1
00401112 je wmain+0A0h (401110h)



[VS 2005 크기 최소화 (/O1)]
vs 2003 과는 달리 TThread::Terminate() 메소드를 inlining 해 버리는 군요. vs 2003 에서는 inlining 이 잘 안되는 경우가 많아서 애먹은 적이 꽤 있는데 2005 에서도 좀 테스트 해봐야 겠네요.
VS 2005 의 크기 최소화는 황당한 최적화를 수행하지 않는군요.. 아주 맘에 듭니다.
inlining 도 더 똑똑해 진것 같고, 무한루프코드를 생성해 내지도 않습니다. 

tt.Terminate();
004010CF cmp byte ptr [ebp-3],1
004010D3 je wmain+89h (4010DFh)
004010D5 mov byte ptr [ebp-3],1
004010D9 cmp byte ptr [ebp-2],1
004010DD je wmain+83h (4010D9h)
댓글에 정성태님께서 말씀하신 volatile 키워드를 이용해서 m_ActiveState 멤버를 선언하는 것이 가장 좋은 해결 방법인것 같습니다.몇시간을 이거 문제점 찾느라 보내고, 테스트 해보느라 시간 보내고.. 뭐 그래도 버린 시간이 아깝지는 않네요.
컴파일러가 최적화를 시키는데 어떤 규칙들이 있을것 같은데 그런 내용들은 어디에 있는지 궁금하네요.
시간날때 한번 봐두는 것도 좋을 것 같은데 말이죠.

-------------- 추가 ------------ 추가 --------------

http://www.debuglab.com/knowledge/volatile.html
debuglab 에 똑같은 내용의 글이 있었네요. :-(
하하..뒷북이었네요...ㅜㅜ (은근히 쑥스럽네여..ㅎㅎ)

덧글|덧글 쓰기|신고

  • 정성태2006-12-15 13:11

    좋은 사례입니다. ^^

    최적화라는 것이 결국 다른 프로그래머의 논리에 의해서 나왔기 때문에... 순간 순간 사람의 머리로 판단하는 것과는 달리, 그와 같은 오류를 낼 수 있습니다.

    따라서, 그러한 점을 보완하기 위해서 ^^ 그 논리를 만든 프로그래머는 또 다른 키워드를 제공한답니다.
    바로 "volatile" 키워드가 그것입니다.

    volatile bool m_ActiveState;

    이라고 하시면, 그와 같은 문제가 해결됩니다. ^^

  • somma2006-12-15 14:57

    정성태 / 포스팅 중에 댓글이 올라와 버렸네요. 감사합니다.
    홈페이지에서 엄청난 포~스가 느껴지시네요. 자주 들려서 공부좀 해야 겠습니다. ^^

'Visual Studio' 카테고리의 다른 글