[OpenCL 입문 시리즈 9편] 다중 디바이스 활용: 한 시스템 내 다양한 하드웨어 자원 사용하기

안녕하세요! 지난 글에서는 디버깅과 프로파일링 기초를 다루며 OpenCL 프로그램을 더 안정적이고 효율적으로 다루는 방법을 살펴봤어요. 이제 거의 시리즈의 끝이 보이는데요. 이번 글에서는 한 시스템 내 여러 디바이스(여러 개의 GPU, CPU, 또는 이종 디바이스)를 동시에 활용하는 방법을 간략히 살펴볼 겁니다.

etc-image-0

CUDA는 보통 하나의 GPU를 다루는 경우가 많지만, OpenCL은 다양한 벤더, 다양한 하드웨어를 동시에 활용할 수 있는 아키텍처적 장점이 있어요. 예를 들어, 한 PC에 NVIDIA GPU와 Intel GPU, 그리고 CPU 디바이스까지 있을 때, 이들을 모두 활용해 연산을 나누어 처리할 수도 있습니다.

 

이번 글에서는 다음 내용을 다룹니다.

  • 여러 디바이스 선택 방법
  • 멀티 디바이스 컨텍스트(Context) 및 커맨드 큐(Command Queue) 생성
  • 디바이스별로 작업을 나누어 실행하는 전략
  • CUDA와 비교: CUDA는 주로 하나의 GPU에 집중, Multi-GPU는 NVLink나 Multi-GPU API 필요
  • 도움이 될만한 참고 링크

1. 다중 디바이스를 왜 사용할까?

하나의 GPU만으로도 충분히 빠를 수도 있지만, 작업량이 엄청날 때는 여러 디바이스를 동시에 활용할 수 있습니다. 예를 들어:

  • 한 디바이스에서 이미지 처리, 다른 디바이스에서 물리 시뮬레이션
  • CPU와 GPU를 함께 활용해 특정 연산은 CPU가, 병렬 연산은 GPU가 담당

이렇게 하면 전체 처리 시간을 단축할 수 있습니다.

2. 멀티 디바이스 컨텍스트와 큐 생성하기

OpenCL에서는 컨텍스트(Context)가 하나 이상의 디바이스를 포함할 수 있습니다. clCreateContext나 C++ Wrapper의 cl::Context 생성 시 std::vector<cl::Device>를 넘겨 여러 디바이스를 지정하면, 해당 컨텍스트를 통해 다중 디바이스 접근이 가능합니다.

// 예: 두 개의 디바이스가 있다고 가정
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
auto platform = platforms.front();

std::vector<cl::Device> devices;
platform.getDevices(CL_DEVICE_TYPE_ALL, &devices);

// 첫 번째와 두 번째 디바이스를 사용해 컨텍스트 생성
std::vector<cl::Device> selectedDevices = { devices[0], devices[1] };
cl::Context context(selectedDevices);

// 각 디바이스용 커맨드 큐 생성
cl::CommandQueue queue1(context, devices[0]);
cl::CommandQueue queue2(context, devices[1]);

이렇게 하면 queue1으로는 첫 번째 디바이스에 작업을, queue2로는 두 번째 디바이스에 작업을 보낼 수 있습니다. 단, 데이터는 서로 다른 디바이스 메모리에 있으므로, 각 디바이스용 버퍼를 별도로 관리하거나, 디바이스 간 데이터 교환을 위해 호스트를 통해 데이터를 주고받아야 할 수도 있습니다.

3. 디바이스 간 작업 분배 전략

가장 간단한 전략은 작업을 “나누어 담기” 하는 방식입니다. 예를 들어, 벡터 덧셈 연산이 있다면 벡터를 반으로 쪼개어 첫 번째 디바이스가 앞부분을, 두 번째 디바이스가 뒷부분을 처리하도록 할 수 있어요.

// 전체 작업 N
int N = 10000;

// 디바이스 1이 0~4999, 디바이스 2가 5000~9999 처리
int half = N/2;
// 디바이스별 버퍼, 커널 세팅...
// queue1.enqueueNDRangeKernel(..., globalSize=half);
// queue2.enqueueNDRangeKernel(..., globalSize=half);

// 두 디바이스가 동시에 실행하도록 커맨드 큐에 명령을 모두 넣은 뒤 기다림
queue1.finish();
queue2.finish();

이렇게 하면 두 디바이스가 병렬로 작업을 처리하게 되어, 이상적으로는 처리 시간이 절반가량 줄어들 수 있습니다(물론 실질적인 성능 향상은 디바이스 성능, 메모리 대역폭, Overhead에 따라 다릅니다).

 

이미지 처리나 과학 계산 등에서 데이터 크기를 기준으로 단순 파티션을 해보거나, 디바이스 성능 특성을 고려해 비율을 다르게 나눌 수도 있습니다(예: 더 빠른 GPU에 70% 할당, 느린 GPU나 CPU에 30% 할당).

4. CUDA와 비교

CUDA에서는 하나의 프로세스가 기본적으로 하나의 GPU를 다루는 게 일반적이고, Multi-GPU 활용을 위해서는 여러 CUDA context를 만들거나 NVIDIA의 Multi-GPU 라이브러리(NCCL) 등을 이용해야 합니다. 반면 OpenCL은 처음부터 멀티 디바이스 사용을 염두에 두고 설계되어, 한 컨텍스트 안에 여러 디바이스를 넣고 관리하기가 상대적으로 수월합니다.

 

물론 실제 구현 난이도는 비슷할 수 있습니다. 디바이스 간 메모리 공유가 거의 없으니, 데이터를 잘 나누고, 결과를 합치는 로직을 명확히 해야 합니다.

5. 참고할만한 자료

  • OpenCL Specification: clCreateContext : 컨텍스트 생성 부분을 보면 여러 디바이스를 넘길 수 있음을 확인할 수 있습니다.
  • CodeVault OpenCL Tutorials : 다중 디바이스 활용에 대한 자세한 예제는 많지 않지만, OpenCL 디바이스 관리에 대한 언급이 있는 일부 영상에서 힌트를 얻을 수 있습니다. (2024-12-06 기준 CodeVault 채널 활성)

6. 마무리

이번 글에서는 OpenCL을 활용해 한 시스템 내 다양한 디바이스를 동시에 활용하는 방법을 살펴봤습니다. 멀티 디바이스 활용은 모든 경우에 반드시 필요하지는 않지만, 특정한 상황(예: 매우 큰 데이터셋 처리, 이종 컴퓨팅 요구)에서 큰 장점이 될 수 있습니다.

 

다음 글(마지막 10편)에서는 시리즈를 마무리하며, 지금까지 배운 내용들을 정리하고, 앞으로 더 배워나갈 수 있는 방향성을 제시하겠습니다. 이로써 OpenCL 입문 로드맵이 마무리될 예정입니다.

유용한 링크 & 리소스

반응형