[완료] 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.
댓글 달기