[완료] C++ 릴리즈 모드에서 멀티스레드의 성능이 하락하는 문제
글쓴이: d8gearz / 작성시간: 일, 2011/01/30 - 12:45오전
공부할겸 테스트로 짜본 소스코드입니다. 테스트할겸 짜느라 주석도 없고, 들여쓰기나 띄어쓰기도 다소 이상합니다만, 조~금 혼란스럽더라도 참고 봐주세요.
참고:
싱글톤 패턴도 연습해볼겸, module_import.hpp에 동적생성 방식의 싱글톤 클래스를 선언하고, 묵시적 import의 형태로 DLL 모듈에서가져와 쓰고있습니다.
소스코드:
#include <iostream>
using namespace std;
#include <windows.h>
#pragma comment(lib, "winmm.lib")
#include <process.h>
#include <tchar.h>
#include "../staticmodule/module_import.hpp"
#pragma comment(lib, "../../../debug/staticmodule.lib")
static int target_loop = 500;
static int target_count = 100000000;
static int count = target_count;
static bool* flag = NULL;
struct arg
{
HANDLE hEvent;
bool* status;
};
unsigned int WINAPI thread_function(void* arg); // 가변인자를 떠올리면 될듯하다
int main()
{
singleton::getInstance(); // 스레드 내부에서 처음 instance를 실행시키면 2개 이상의 인스턴스가 발생할수 있으므로 미리 초기화시킨다.
int thread_count = 0;
cout << "enter thread count\n";
cin >> thread_count;
count = target_count/thread_count;
{
unsigned int hThread;
unsigned threadID;
// event2는 각 스레드의 동작을 멈추는 역할을 수행한다. 즉 매 프레임마다
// 스레드갯수 만큼 WaitFor...와 인카운트 한다. 이걸 auto로 잡으면,
// 첫번째 스레드 인카운터시에 true->false로 변경되어버리고 만다.
singleton::getInstance()._thread = new bool[thread_count];
for(int n=0; n < thread_count ;n++)
singleton::getInstance()._thread[n] = false;
HANDLE* frame_next = new HANDLE[thread_count];
flag = new bool[thread_count];
arg* arguments = new arg[thread_count];
for(int n=0; n < thread_count ;n++)
{
frame_next[n] = CreateEvent(NULL, FALSE, FALSE, NULL);
arguments[n].hEvent = frame_next[n];
arguments[n].status = (flag+n);
hThread = _beginthreadex(NULL, 0, thread_function, (void*)(arguments+n), 0, &threadID);
CloseHandle((HANDLE) hThread);
}
cout << "test start ::: \n\t" << thread_count << " thread... each count is " << target_count/thread_count << "\n";
int start = timeGetTime();
for(int n=0; n < target_loop ;n++)
{
// 한프레임이 완료되었음 원래는 스테이터스를 받아서 TIMED_OUT인지 WAIT_0인지 판가름 해야 겠지
// 역기서, 가장 부하가 많이 걸린 스레드와 적게 걸린 스레드를 찾아서 재 할당시킨다(각 스레드는 현재 PAUSE 상태
// 각 스레드 이벤트SetOff
bool check = false;
while(!check) // 도스라서 이런식으로 구성했지만, 윈APP라면, 메시지루프에 빠져들어갔다 나오는 식의 동작을 취해야 할것이다.
{
int n1 = 0;
for(; n1 < thread_count ;n1++)
{
if(!(flag[n1])) continue; // for 밖으로 나감
check=true;
}
};
// status 초기화
for(int n2=0; n2 < thread_count ;n2++)
flag[n2] = false;
for(int n2=0; n2 < thread_count ;n2++)
SetEvent(frame_next[n2]);
}
singleton::getInstance().setDisable();
int endtime = timeGetTime() - start;
Sleep(1000);
cout << "\n\n";
cout << "end thread... \n time is " << endtime << endl;
delete [] frame_next;
}
char dummy;
cout << "test over. press any character.\n";
cin >> dummy;
return 0;
}
// 작업자 스레드 함수
unsigned int WINAPI thread_function(void* thread_event)
{
arg argument = *((arg*)(thread_event));
while(singleton::getInstance().getEnable())
{
int a = 0;
for(int iterator=0; iterator < count ;iterator++)
{
a = iterator + a;
}
*(argument.status) = true;
WaitForSingleObject(argument.hEvent, INFINITE);
}
_endthreadex(0); // 메모리해제
return 0;
}질문은 이렇습니다.
디버그 모드, 릴리즈 모드에서 실행이 모두 잘됩니다.
디버그 모드에서 스레드를 늘리면, 늘어난 스레드에 따라서 프로그램 실행시간또한 줄어듭니다. (한마디로, 원했던 대로 잘 작동합니다) CPU 사용량도 비례해서 올라가구요.
그런데, 같은 소스코드를 릴리즈모드에서 빌드하면, 이게 이상해져버리네요.
스레드2개가 가장 적은 시간이 나옵니다. 3스레드를 돌리면 오히려 1스레드일때보다 시간이 더 늘어나네요. (참고로, CPU는 쿼드코어 8스레드구요. 디버그모드에서는 1~8스레드까지 늘릴수록 실행속도도 비례해서 빨라지는 걸 확인했습니다)
제가 뭔가 잘못한게 있는지, 아니면 릴리즈 모드에서 따로 옵션을 켜야 하는게 있는지 도저히 모르겠어서 질문 남겨봅니다.
부탁드려요. ㅠ_ㅠ
Forums:


소스가 길어 보지는 못하겠지만...
그냥 글만 보면 실행시간보다 쓰레드 생성시간이 많아서 스레드를 늘릴 수록 더 시간이 늘어난것 일듯 합니다. 디버그 모드에서는 이것저것 딴거 하는 거 많아서 실행시간이 길어져 스레드를 늘리는 만큼 줄어들지만요. 디버그모드도 아마 쓰레드 엄청 늘리면 실행시간이 오히려 늘어날 겁니다.
CPU 사용량이 좀 다릅니다.
답글 감사합니다~
그런데, 디버그 모드에서는 스레드 갯수에 따라서 거의 100%까지도 CPU를 혹사시키는 반면에, 릴리즈모드에서는 항상 30%이상은 안되더라구요.
그래서 실행시간이 길어지는 것 같은데, 왜 그런지 도통 모르겠네요...
해결했습니다.
스레드함수를 현재 1억번 += 연산을 하고 있는데, 이부분을 조금더 부하가 걸리는 작업(CString에 LPCSTR로 생성자 호출)으로 대체하니 CPU 사용률과 속도가 디버그 모드 처럼돌아왔습니다.
원인은, 현재 스레드 동기화를 위해서 한 작업이 끝나면 스레드 전부가 대기상태에 들어가는데요, 아마도 릴리즈모드에 따른 최적화로 for문의 += 연산이 순식간에 처리(내지는 릴리즈모드에서 연산이 빨라졌다던가..) , 연산보다는 대기상태에 따른 지연시간이 실행시간의 대부분을 차지하게 되어서 인 것 같습니다.
실제로, 위의 소스코드를 사용하면 CPU사용량이 0%에 달할때도 있더군요.
CPU 항상 30%에 머물렀던건 주 스레드에서의 작업이 약 30%정도였기때문으로 보입니다. (연산이 순식간에 끝난다면 주스레드에서 스레드감지 하는 작업이 실행시간의 대부분을 잡을 테니까요)
스레드는 적절한 상황에서만 사용해야 한다고 문서로만 읽었는데, 뼈저린 체험(?)을 해보았네요. 아직 배울게 너무 많은것 같습니다. :)
답변해주신 kaeri17님 감사드리고, 혹시라도 저와 같은 분들을 위해서 포스트는 남겨둘께요.
연산이 최적화 되었을 확률이 있습니다. 상수 덧셈은
연산이 최적화 되었을 확률이 있습니다. 상수 덧셈은 컴파일시간에 계산 처리해버릴 수 있거든요 최적화 옵션에 따라... ( 결과가 결정되어있을때 )
Neogeo - Future is Now.
댓글 달기