[완료] C++ 릴리즈 모드에서 멀티스레드의 성능이 하락하는 문제

d8gearz의 이미지

공부할겸 테스트로 짜본 소스코드입니다. 테스트할겸 짜느라 주석도 없고, 들여쓰기나 띄어쓰기도 다소 이상합니다만, 조~금 혼란스럽더라도 참고 봐주세요.

참고:
싱글톤 패턴도 연습해볼겸, 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스레드까지 늘릴수록 실행속도도 비례해서 빨라지는 걸 확인했습니다)

제가 뭔가 잘못한게 있는지, 아니면 릴리즈 모드에서 따로 옵션을 켜야 하는게 있는지 도저히 모르겠어서 질문 남겨봅니다.

부탁드려요. ㅠ_ㅠ

kaeri17의 이미지

그냥 글만 보면 실행시간보다 쓰레드 생성시간이 많아서 스레드를 늘릴 수록 더 시간이 늘어난것 일듯 합니다. 디버그 모드에서는 이것저것 딴거 하는 거 많아서 실행시간이 길어져 스레드를 늘리는 만큼 줄어들지만요. 디버그모드도 아마 쓰레드 엄청 늘리면 실행시간이 오히려 늘어날 겁니다.

d8gearz의 이미지

답글 감사합니다~

그런데, 디버그 모드에서는 스레드 갯수에 따라서 거의 100%까지도 CPU를 혹사시키는 반면에, 릴리즈모드에서는 항상 30%이상은 안되더라구요.

그래서 실행시간이 길어지는 것 같은데, 왜 그런지 도통 모르겠네요...

d8gearz의 이미지

스레드함수를 현재 1억번 += 연산을 하고 있는데, 이부분을 조금더 부하가 걸리는 작업(CString에 LPCSTR로 생성자 호출)으로 대체하니 CPU 사용률과 속도가 디버그 모드 처럼돌아왔습니다.
원인은, 현재 스레드 동기화를 위해서 한 작업이 끝나면 스레드 전부가 대기상태에 들어가는데요, 아마도 릴리즈모드에 따른 최적화로 for문의 += 연산이 순식간에 처리(내지는 릴리즈모드에서 연산이 빨라졌다던가..) , 연산보다는 대기상태에 따른 지연시간이 실행시간의 대부분을 차지하게 되어서 인 것 같습니다.
실제로, 위의 소스코드를 사용하면 CPU사용량이 0%에 달할때도 있더군요.
CPU 항상 30%에 머물렀던건 주 스레드에서의 작업이 약 30%정도였기때문으로 보입니다. (연산이 순식간에 끝난다면 주스레드에서 스레드감지 하는 작업이 실행시간의 대부분을 잡을 테니까요)

스레드는 적절한 상황에서만 사용해야 한다고 문서로만 읽었는데, 뼈저린 체험(?)을 해보았네요. 아직 배울게 너무 많은것 같습니다. :)

답변해주신 kaeri17님 감사드리고, 혹시라도 저와 같은 분들을 위해서 포스트는 남겨둘께요.

neogeo의 이미지

연산이 최적화 되었을 확률이 있습니다. 상수 덧셈은 컴파일시간에 계산 처리해버릴 수 있거든요 최적화 옵션에 따라... ( 결과가 결정되어있을때 )

Neogeo - Future is Now.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.