본문 바로가기

병렬프로그래밍

TBB & PPL의 task_group(task)를 사용해보자..

기존에 쓰래드 생성시 사용했었던 CreateThread와 _BeginThread도 있지만..
병렬프로그래밍에서는 task라는게 새로 생겼습니다..

코드부터..

 
#include <windows.h>
#include <tbb/tbb.h>
#include <iostream>
#include <time.h>
#include <array>
#include <queue>
#include <ppl.h>
#include <concurrent_queue.h>
#include <algorithm>
using namespace std;
const int MAX_LOOP = 10000000;
const int THREAD_COUNT = 4;
queue<int> queTest;
tbb::concurrent_queue<int> queParallelTest1;
tbb::concurrent_bounded_queue<int> queParallelTest2;
Concurrency::concurrent_queue<int> queParallelTest3;

class CRITICAL_SECTION_T
{
public:
  CRITICAL_SECTION_T(){InitializeCriticalSection(&cs);}
  ~CRITICAL_SECTION_T(){DeleteCriticalSection(&cs);}

  void Entry(){EnterCriticalSection(&cs);}
  void Leave(){LeaveCriticalSection(&cs);}
private:
  CRITICAL_SECTION cs;
};

CRITICAL_SECTION_T critical;
DWORD WINAPI WorkerThread( LPVOID parameter )
{
  critical.Entry();
  static INT64 nSum = 0;
  while(!queTest.empty())
  {
    nSum += queTest.front();
    queTest.pop();
  }
  critical.Leave();
  return 1;
}

DWORD WINAPI ParallelWorkerThread( LPVOID parameter )
{
  int nValue = 0;
  static INT64 nSum = 0;
  if((INT_PTR)parameter == 1)
  {
    while(!queParallelTest1.empty())
    {
      if(queParallelTest1.try_pop(nValue))
        nSum+= nValue;
    }
  }
  else if((INT_PTR)parameter == 2)
  {
    while(!queParallelTest2.empty())
    {
      if(queParallelTest2.try_pop(nValue))
        nSum+= nValue;
    }
  }
  else if((INT_PTR)parameter == 3)
  {
    while(!queParallelTest3.empty())
    {
      if(queParallelTest3.try_pop(nValue))
        nSum+= nValue;
    }
  }

  return 1;
}

int _tmain(int argc, _TCHAR* argv[])
{
  cout << "push queue \n";
  for(int i = 0 ; i< MAX_LOOP; i++)
  {
    queTest.push(i);
    queParallelTest1.push(i);
    queParallelTest2.push(i);
    queParallelTest3.push(i);
  }
  
  cout << "pop queue test start\n";
  tbb::task_scheduler_init init;
  array<HANDLE,THREAD_COUNT> hThread = {0,};

  time_t start = clock();

  for(int i = 0; i< THREAD_COUNT; i++)
  {
    hThread[i] = 	CreateThread( 0, 0, WorkerThread, nullptr, 0, 0 );
  }
  WaitForMultipleObjects( THREAD_COUNT,hThread.data(),true, INFINITE);
  cout << "CreateThread테스트" << " : " << (clock() - start) / double(CLOCKS_PER_SEC) << endl;

  start = clock();
  tbb::task_group tasks1;
  tasks1.run([]{ParallelWorkerThread((LPVOID)1);});
  tasks1.wait();
  cout << "tbb::task테스트(tbb::concurrent_queue)" << " : " << (clock() - start) / double(CLOCKS_PER_SEC) << endl;

  start = clock();
  tbb::task_group tasks2;
  tasks2.run([]{ParallelWorkerThread((LPVOID)2);});
  tasks2.wait();
  cout << "tbb::task테스트(tbb::concurrent_bounded_queue)" << " : " << (clock() - start) / double(CLOCKS_PER_SEC) << endl;

  start = clock();
  tbb::task_group tasks3;
  tasks3.run([]{ParallelWorkerThread((LPVOID)3);});
  tasks3.wait();
  cout << "tbb::task테스트(Concurrency::concurrent_queue)" << " : " << (clock() - start) / double(CLOCKS_PER_SEC) << endl;
  return 0;
}

ps.
task도 CreateThread처럼 사용할 갯수만큼 생성해줘야 되는지 알고 여러개 생성해줬는데..
크리티컬 섹션을 건 queue보다 속도가 나오질 않는게 이상해서 task에 대해 자료를 찾아보던중..
task는 내부적으로 cpu 코어 갯수만큼 자동생성해준다는걸 알고 위처럼 1개만 생성하니 성능적으로 잘 나오네요..

그리고 참고적으로 concurrent_quque중에서 뭐가 빠른가 찾아보던중
ppl에 포함되어있는 queue가 tbb보다는 빠르다는것을 알게되었습니다.

아마도. vs2010에서 포함된 RValue(우측값참조)때문에 그런게 아닐지 생각해봅니다..
vs2008에서 vs20100으로  stl사용시 vs2010으로 사용하는것만으로도 몇배의 성능향상이 있었으니깐요.. 

결과적으로는 task_group자체는 ppl이 tbb보다는 빨랐지만 거의 무의미한 수준이였고..
queue는 ppl이 월등했습니다..

스샷 첨부..