다음과 같은 코드가 있습니다. 그냥 for문 돌려서 CPU를 소모하게 하는 코드입니다.


테스트 코드 1
void foo(int loopCnt) // 2000000000
{
  int cnt = loopCnt;
  for (int i = 0; i < cnt; i++);
}


loopCnt의 값이 무지막지하게 큰 값(20억) 을 주고 상기 코드를 돌리면 CPU를 100% 먹게 됩니다. 물론 CPU가 4개라면 25%를 먹게 되죠. foo()를 실행하기 이전에 tick(시각)을 구하고 foo()를 실행하고 나서 tick을 구한 다음에 그 차이를 출력해 봅니다. 제 컴퓨터에서는 7.878초가 걸립니다. 대략 8초가 나왔다는 얘기입니다.


[thread 갯수 1개]

7878


자, 그럼 상기의 foo() 함수를 여러개의 thread를 이용해서 동시에 실행을 해 봅니다. 그러면 foo() 함수를 실행하는데 걸리는 시각은 다음과 같이 나옵니다.


테스트 결과 1


[thread 갯수 1개]

7878


[thread 갯수 2개]

8689

8689


[thread 갯수 3개]

8799

8830

8815


[thread 갯수 4개]

9095

9282

9345

9314


thread 2개를 돌리면 약 8.6초, thread 3개를 돌리며 약 8.8초 정도 걸리는군요. 즉 foo 함수 내부의 코드는 다른 thread와 자원과 공유하는 부분이 없어서, 각각의 thread 수행에 지장을 주지 않기 때문에 thread의 완료 시점은 thread의 갯수와는 별 상관이 없이 비슷하게 나오는 것으로 판단할 수 있습니다.


자, 이제는 다른 테스트를 해 보겠습니다.


테스트 코드 2
for (int i = 0; i < callCnt; i++) // 1000000000
{
  foo(loopCnt); // 1
}


loopCnt값은 아주 작은 값(1)을 주고, callCnt에는 아주 큰 값(10억)을 주어 실행을 합니다. 물론 상기 코드도 thread를 동시에 여러개 수행하여 경과 시간을 측정을 해 봅니다.


테스트 결과 2


[thread 갯수 1개]

9080


[thread 갯수 2개]

12184

12215


[thread 갯수 3개]

16115

17129

17254


[thread 갯수 4개]

24555

24898

25132

25304


테스트 결과 1 과 테스트 결과 2를 비교해 보면 조금 다르게 나옵니다. 결과 1은 thread가 늘어 나도 수행 완료 시간에는 별 영향을 주지 않는 반면에, 결과 2는 thread의 갯수와 수행 완료 시간은 상관 관계에 있다는 것으로 결과가 나옵니다.


테스트 코드 2를 보면 각 thread간의 간섭 현상(자원을 공유한다든지 하는)이 없음에도 불구하고 thread가 늘어 남에 따라 수행 시간이 늘어 나는 것은 이해가 되지 않습니다. 원인을 도통 알 수가 없네요.


실행 환경


CPU : Intel(R) Core(TM) i5 CPU U 470 @ 1.33GHz

Memory : 3GB

OS : Windows 7 32bit

Microsoft Visual Studio 2005 / Release Mode / Optimization Disabled(/Od)


소스 및 실행 파일 첨부합니다. call_loop_thread_test.zip (for windows only)    multi_platform_call_loop_thread_test.zip  (for windows, linux and boost)


#include <conio.h>
#include <list>
#include <stdio.h>
#include <windows.h>

typedef struct _Param
{
  int callCnt;
  int loopCnt;
  int threadCnt;
} Param;

void foo(int loopCnt)
{
  int cnt = loopCnt;
  for (int i = 0; i < cnt; i++);
}

DWORD __stdcall threadProc(LPVOID p)
{
  Param* param = (Param*)p;

  int callCnt = param->callCnt;
  int loopCnt = param->loopCnt;

  DWORD begTick = GetTickCount();
  
  for (int i = 0; i < callCnt; i++)
  {
    foo(loopCnt);
  }
  
  DWORD endTick = GetTickCount();

  printf("%d\n", endTick - begTick);
  return 0;
}

void usage()
{
  printf("call_loop_thread_test <call count> <loop count> <thread count>\n");
  printf("example : call_loop_thread_test 100 100 2\n");
}

int main(int argc, char* argv[])
{
  if (argc != 4)
  {
    usage();
    return 0;
  }

  Param param;

  param.callCnt   = atoi(argv[1]);
  param.loopCnt   = atoi(argv[2]);
  param.threadCnt = atoi(argv[3]);

  std::list<HANDLE> threadList;

  for (int i = 0; i < param.threadCnt; i++)
  {
    DWORD threadID;
    HANDLE threadHandle = 
      CreateThread(
        NULL,
        0,
        &threadProc,
        &param,
        0,
        &threadID);
    threadList.push_back(threadHandle);
  }

  for (std::list<HANDLE>::iterator it = threadList.begin(); it != threadList.end(); it++)
  {
    HANDLE threadHandle = *it;
    WaitForSingleObject(threadHandle, INFINITE);
  }

  threadList.clear();
  return 0;
}


call_loop_thread_test1_sc.png

call_loop_thread_test2_sc.png


도대체 원인이 뭘까요? 정말 모르겠습니다. -_-;