C++에서 환경의 쓰레드 개수 구하기

C++에서 쓰레드 개수 구하기

C++11 이후

C++11에서 지원하는 라이브러리를 이용한다면 쉽게 쓰레드 개수를 구할 수 있다.
thread 라이브러리의 std::thread::hardware_concurrency 메소드를 이용하자. hardware_concurrency()는 현재 시스템의 쓰레드 개수를 반환한다. 만약 쓰레드 개수를 알 수 없다면, 0을 반환한다.
std::thread::hardware_concurrency 는 이식 가능한 (포터블)한 코드로써 리눅스, 맥os, 윈도우에서 모두 사용 가능하다.

static unsigned int hardware_concurrency() noexcept;

사용 예시

#include <iostream>
#include <thread>

int main() {
    const auto n = std::thread::hardware_concurrency();
    std::cout << n << " concurrent threads are supported.\n";
}

가능한 출력:

8 concurrent threads are supported.  

이는 해당 컴퓨터가 8개의 쓰레드를 갖고 있다는 말이다.

더 자세한 내용을 보고 싶으면 cppreference를 참고하자.

C++11 전

모던 C++ 이전에는 포터블한 방법은 없다. 운영체제 맞추어서 코드를 다시 써줘야 한다. 클래식 C++의 한계이다.

OpenMP Library 사용

C++에서는 병렬처리를 쉽게 도와주는 OpenMP Library가 있다. OpenMP는 인터페이스로써 병렬 처리를 매우 쉽게 구현할 수 있도록 도와준다. 쓰레드의 처리, 특히 태스크 기반 동적 쓰레드 병렬 연산은 구현하기가 매우 까다롭고 귀찮다. 특별한 이유가 있지 않는 한, OpenMP 라이브러리를 사용해보는 것을 고려할 수 있다.

OpenMP 라이브러리를 이용하기 위해서 해야 할 것은 아무것도 없다. 거의 대부분의 IDE에 OpenMP는 기본 탑재되어 있기 때문이다.

#include <omp.h>

int main() {
  const int num_threads = omp_get_num_procs();
  // use the value of num_threads for your application
  return 0;
}

Win32

SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
const int num_threads = sysinfo.dwNumberOfProcessors;

Linux, MacOS

#include <unistd.h>

int main() {
  const int num_threads = sysconf(_SC_NPROCESSORS_ONLN);
  // use the value of num_threads for your application
  return 0;
}

HPUX

const int num_threads = mpctl(MPC_GETNUMSPUS, NULL, NULL);

FreeBSD, MacOS X, NetBSD, OpenBSD, etc.

int mib[4];
int num_threads;
std::size_t len = sizeof(numCPU); 

/* set the mib for hw.ncpu */
mib[0] = CTL_HW;
mib[1] = HW_AVAILCPU;  // alternatively, try HW_NCPU;

/* get the number of CPUs from the system */
sysctl(mib, 2, &num_threads, &len, NULL, 0);

if (num_threads < 1) 
{
    mib[1] = HW_NCPU;
    sysctl(mib, 2, &numCPU, &len, NULL, 0);
    if (num_threads < 1)
        num_threads = 1;
}

그 외 다양한 플랫폼에서 제공하는 API가 있을 것이다. 사실 C++11의 thread와 OpenMP만 쓰는 본인 역시 hardware_concurrency와 omp_get_num_procs 외엔 다 처음 보는 방법이다.

적절한 쓰레드 개수?

여담으로 코어와 쓰레드는 다를 수 있다. 예전에는 코어와 쓰레드가 같은 경우가 대부분이었지만, 이제는 대부분의 CPU가 기본적으로 하이퍼쓰레딩을 지원하기 때문에 (쓰레드 수) = 2 * (코어 수) 가 성립하는 경우가 많다.

한편 적절한 쓰레드 개수에 대한 논쟁이 많다. 병렬 처리는 컴퓨터 공학의 꽃이라 불리우는 만큼 최적의 성능을 내주는 쓰레드 개수를 찾기 위해서 일반화된 공식은 존재하지 않고, 각 시스템, 태스크를 이해한 뒤 실험과 측정을 통해서 최적의 쓰레드 개수를 찾아야 한다.

그럼에도 불구하고, 만약 자신이 만든 C++ 코드로 자신의 CPU를 혹사시키고 싶다면, 자신이 가진 (쓰레드 수) * 2 + 1 만큼의 쓰레드로 연산을 시키자. 그러면 아마 CPU 점유율 100%를 뽑아낼 수 있다.

  const auto thread_numbers = 2 * std::thread::hardware_concurrency() + 1;
  ///> 자신이 가진 쓰레드 수 * 2 + 1 만큼이 저장될 것이다.

이 이상 쓰레드 수를 확보하는 것은 의미가 없을 수 있는데, 쓰레드의 물리적 수는 한정되어 있기 때문이다. 쓰레드 개수가 12개인데 24개의 쓰레드를 할당시키면, 한 쓰레드가 2개의 쓰레드를 왔다갔다 스위칭하며 일을 처리한다. 이 스위칭 때문에 오히려 성능이 더 떨어질 수 있다. 즉 무조건 쓰레드 개수가 많은 것이 호사는 아니라는 뜻이다.

 

프로그램이 돌아가는 환경과 태스크 등에 맞추고, 적절한 실험을 통해서 최적의 쓰레드 개수를 찾아내자.