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

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 입문 로드맵이 마무리될 예정입니다.
유용한 링크 & 리소스
- OpenCL 공식 문서 : 멀티 디바이스 사용 관련 API 참조
- Khronos OpenCL Registry : 함수별 상세 스펙
- CodeVault YouTube : OpenCL 관련 실습 영상(멀티 디바이스 활용 힌트)
'개발 이야기 > OpenCL' 카테고리의 다른 글
[OpenCL 입문 시리즈 10편] 마무리 및 향후 학습 방향 (0) | 2024.12.16 |
---|---|
[OpenCL 입문 시리즈 8편] 디버깅 & 프로파일링 기초: 문제 상황 파악과 성능 병목 진단하기 (0) | 2024.12.14 |
[OpenCL 입문 시리즈 7편] 성능 최적화 맛보기: 워크그룹, 메모리 접근 패턴, 프로파일링 기초 (0) | 2024.12.13 |
[OpenCL 입문 시리즈 6편] 간단한 이미지 처리 예제: 그레이스케일 변환하기 (0) | 2024.12.12 |
[OpenCL 입문 시리즈 5편] C++20/23로 깔끔하게 래핑하기 (0) | 2024.12.11 |