{{{+3 {{{#ffffff Open Computing Language}}}}}} [1] |
[clearfix]
1. 개요
엔비디아 GPU 전용인 CUDA에 대응하기 위해 2009년 8월에 발표된 GPGPU 프로그래밍 전용 API.2. 배경
개발사는 Apple로 상표권도 가지고 있다. Apple은 2008년 12월 8일에 어느 정도 틀을 잡아놓고 NVIDIA, AMD, 인텔 등과 같이 다듬어서 OpenGL 등을 개발하는 크로노스 그룹에 맡겼다. 그 후 크로노스 그룹은 2009년 8월 28일에 1.0을 개발하였고, Mac OS X Snow Leopard와 같이 공개되었다.Apple은 OpenCL 1.0을 정식 공개하면서 이렇게 말했다.
Snow Leopard further extends support for modern hardware with Open Computing Language (OpenCL), which lets any application tap into the vast gigaflops of GPU computing power previously available only to graphics applications. OpenCL is based on the C programming language and has been proposed as an open standard.
Snow Leopard는 OpenCL로 현대 하드웨어에 대한 지원을 확장하였습니다. 이전에는 GPU의 방대한 기가플롭스 계산 능력을 그래픽 애플리케이션에만 사용해 왔지만, OpenCL을 통하여 이제 어떠한 응용 프로그램에서도 끌어와 쓸 수 있습니다. OpenCL은 C 프로그래밍 언어에 기반하고 있으며, 개방형 표준으로 제안되었습니다.
Snow Leopard는 OpenCL로 현대 하드웨어에 대한 지원을 확장하였습니다. 이전에는 GPU의 방대한 기가플롭스 계산 능력을 그래픽 애플리케이션에만 사용해 왔지만, OpenCL을 통하여 이제 어떠한 응용 프로그램에서도 끌어와 쓸 수 있습니다. OpenCL은 C 프로그래밍 언어에 기반하고 있으며, 개방형 표준으로 제안되었습니다.
그러나, 2018년에 출시된 macOS 10.14 Mojave/iOS 12부터 Apple은 OpenGL과 OpenCL의 지원을 중단하였다. 2014년에 발표된 자체 그래픽 API인 Metal을 밀어주기 위함으로 보인다.
3. 특징
#!syntax cpp
kernel void
arrAdd(global const int *src1, global const int *src2, global int *dest)
{
const uint index = get_global_id(0);
dest[index] = src1[index] + src2[index];
return;
}
OpenCL용 프로그램은 C언어를 기반으로한 확장으로 이루어져 C언어에 익숙한 경우 직관적으로 작성할 수 있으며[2] 이를 통해 OpenCL을 지원하는 장비는 CPU든 GPU든 혹은 그 이외의 연산 장비든 모두 가져다 동시에 쓸 수 있다[3]. 다만 후발주자라서 그런지 많이 알려져 있지 않다. OpenCL은 공개 규격(API)일 뿐이지 소스가 공개된 게 아니며, 구현은 전적으로 개발사에 달려 있다. CUDA는 NVIDIA 하드웨어에서만 동작한다는 단점을 가지고 있어 장래가 기대된다고 보는 사람도 있고, AMD 측과 인텔 측에선 OpenCL을 밀고 있다.
현재 클로즈드 소스 드라이버 중에서는 AMD APP SDK 3.0이 OpenCL 2.0을 지원한다. NVIDIA는 자사에서 먼저 개발된 CUDA를 밀고 있다 보니 2.0에 대한 지원이 더디었고 2016년 이후 불완전하게나마 지원하던 상태에 그쳤지만[4] OpenCL 3.0이 출시되고 465버전부터 3.0까지 지원하기 시작했다. 그에 비하면 오픈소스 진영의 mesa는 OpenCL 2.0을 지원한다.
OS X의 경우, 10.6 Snow Leopard부터 기본적으로 OpenCL을 지원하고 있으며, 따라서 별도의 SDK/드라이버 설치 등이 필요하지 않다. 다만 1.1이 나왔음에도 불구하고 버전이 계속 1.0에 머물러 있으므로 굳이 1.1을 사용하고 싶다면 별도로 드라이버 및 SDK를 설치해야 한다. 또한 일부 헤더 파일을 include할 때 다소 차이가 있다.
GPU만 제어하는 CUDA에 비해 여러 가지 장비를 동시에 관리해야 하므로, 아무래도 함수 호출이 많이 길어지고 초기화 과정도 하나하나 다 해 줘야 하는 결점이 있다. 그렇기 때문에 코드 길이가 CUDA 코드에 비해 압도적으로 길고, 명령어가 많아서 아무래도 처음 배우는 사람들에게는 문턱이 높다.[5] 대신 여러 장비를 동시에 통제할 수 있고, 그 방법 또한 CUDA보다 세련된 면이 있다.[6] 그리고 CUDA에 비해 OpenGL과 결합시키기 좋다. 다만, 게임처럼 OpenGL을 적극 활용하는 환경에서는 장치의 초기화 문제로 대부분의 드라이버에서 딜레이가 생기므로, 간단한 행렬 연산은 CPU의 SSE/AVX나 NEON와 같은 SIMD 확장을 이용하고, 꽤나 무거운 계산이라면 GLSL Compute Shader를 이용하는 것이 낫다.
즉 OpenCL은 CUDA에 비해 하드웨어 범용성이 좋고 소프트웨어적으로도 다양한 응용이 가능하지만 대신 최적화에 한계가 있다고 볼 수 있다.
PyOpenCL이라는 파이썬 wrapper를 사용하여 편리하고 간결하게 사용할 수 있다. 관심이 있다면 찾아서 배워 보자.
Java 또한 OpenCL을 사용할 수 있도록 wrapper가 존재한다. 관심있는 사람은 jocl 공식 홈페이지에서 간단하면서 좋은 샘플들을 볼 수 있다. (당연히 메이븐으로 간단하게 라이브러리 추가가 된다)
현재는 스마트폰에서도 OpenCL을 지원하고 있긴 하다. 다만 OpenGL과의 OpenGL ES의 관계처럼 풀 데스크톱 OpenCL은 아니고, OpenCL ES를 지원한다. 안드로이드의 경우에는 SDK를 지원하며, 최근 안드로이드 스마트폰의 경우는 대부분 Open CL 2.0 Full profile을 지원한다. iOS의 경우 4.3 버전에 힌트를 넣어 놨으며, 5.0 버전부터는 프라이빗 라이브러리 안에 박아넣고 있었다가 iOS 12부터 지원을 중단했다.
4. 역사
2010년 6월 14일 OpenCL 1.1 버전이 발표되어 OpenGL과의 상호 운용성이 개선되었다.2011년 11월 15일 OpenCL 1.2 버전이 발표되었다. OpenGL뿐만 아니라 DirectX의 일부 기능과 공유가 가능해졌다.
2013년 11월 18일 OpenCL 2.0 최종 스펙을 발표했다. 그리고 NVIDIA, AMD 각각 SDK를 제공한다. (NVIDIA 는 CUDA 툴킷에 포함하여 제공, AMD 는 자체 IDE를 제작함) 2.0에 신기술인 HSA가 추가됐으며, AMD APU 칩셋은 2014년 초에 출시한 카베리부터, 인텔은 브로드웰부터 지원된다.
2015년 3월 GDC에서 Vulkan API와 함께 OpenCL 2.1을 발표하여 SPIR-V와의 연동을 소개했는데 이후에 출시된 인텔 스카이레이크, 카비레이크가 현재 OpenCL 2.1이 아닌 2.0 버전에 머물고 있다. 그리고 11월 18일에 OpenCL 2.1의 최종 스펙이 발표되었다.
2017년 5월 16일 SPIR-V 1.1과 연동 가능한 OpenCL 2.2가 최종 발표되었다.
2020년 4월 27일 OpenCL 3.0 표준이 발표되었다. OpenCL 2.x 에서 추가된 모든 기능들이 Optional 로 변경되고 사실상 다시 OpenCL 1.2 로 돌아왔는데 가뜩이나 빈약한 환경에서 플랫폼의 파편화만 유발한다고 비판하는 목소리가 높다.
5. 단점
- 2.0 미만 버전에서는 OpenCL은 호환성 확보를 위해 Online Compile 이라는 방법을 사용하는데 소스 코드를 불러와 그걸을 즉석에서 컴파일하여 바이너리를 만드는 방식이다. 문제는 이런 방식으로는 소스 코드 공개를 막을 방법이 없다. 소스코드를 난독화하더라도(암호화, 압축 등) 소스를 컴파일하여 바이너리를 만드는 그 함수에 반드시 원본 소스를 집어넣어야 하기 때문이다. 그래서 이 함수만 잘 추적한다면 원본 소스를 통째로 도둑맞게 된다. 그렇다고 미리 컴파일된 바이너리를 로드하는 방법을 쓰자니, 각 아키텍처에 맞게 컴파일된 바이너리라 호환성을 보장할 수 없다. 이 상황에서 유일한 방법은 각각 아키텍처마다 일일이 바이너리를 만드는 것이다. 다른 벤더 전용 GPGPU 코드들의 경우 컴파일러를 한차례 거친 바이트코드를 하드웨어에 맞게 재컴파일을 하는 방식을 사용하고 있어 평문으로 된 소스코드를 분석하는것과 컴파일러의 최적화 패스를 거친 바이트코드를 분석하는 난이도는 차이가 분명히 존재한다.
OpenCL 2.0 부터는 Vulkan API의 SPIR-V를 사용할 수 있고 2.1 부터 해당 기능은 Core Feature 로 편입되었다. - 버전의 파편화가 심하다. OpenCL 2.0은 메이저 회사에서 모두 지원하지 않았다. 특히 NVIDIA는 3.0 표준이 나올 때 까지도 1.2 만을 지원하였다. 또한 2.0 위의 핵심 기능들을 Optional로 처리하는 행위 등으로 말만 2.0 지원이지 확장 기능을 지원하지 않는 물건들도 많았으며 3.0은 아예 1.2의 기능만 Core Feature로 처리하고 SPIR 바이트코드 컴파일과 같은 것들을 모두 Optional로 바꿔버렸다. 이 때문에 OpenCL 3.0은 2.0의 슈퍼셋이 아니며 CUDA를 밀고 있는 NVIDIA의 OpenCL 3.0지원 드라이버는 1.2지원때와 별 다른 상황이 아니다.
- OpenGL과 같은 범용이고 구현 없이 명세만 되어 있는 특성상 OpenCL 드라이버 회사마다 성능이 제각각이며 버그도 튀어나온다. 이러한 문제는 OpenCL 자체의 문제라기 보다는 이를 구현하는 벤더의 드라이버 퀄리티 문제긴 하나 생태계 전반적으로 보면 분명한 단점이다.
[1] 한국어로는 개방형 범용 병렬 컴퓨팅 프레임워크.[2] 물론 하드웨어, 특히 GPU의 경우 연산유닛과 구조적 특성에 맞게 코드를 짜야 한다. 대표적인 예로 if와 같은 분기문과 루프문을 최대한 사용하지 않는 것이 그 예. 전통적인 CPU 프로그램들과 달리 GPU의 경우 위 코드와 같은 저런 작은 서브루틴들이 최소 수십에서 수만개의 스레드로 대규모 병렬연산(Massively Parallel)으로 동작하게 되며, 제어유닛이 빈약한 GPU와 같은 매니코어 프로세서에서 CPU프로그래밍과 같이 분기문으로 서로 다른 동작을 하는 루틴이 포함된 코드의 경우 else statement 만큼의 성능을 깎아먹는다고 보면 된다.[3] 이를 통해 FPGA 등을 통해 연산 유닛들을 구현하고 OpenCL 표준에 맞춘다면 그걸 OpenCL 연산 유닛으로 써먹는 것 또한 가능하다. CL_DEVICE_TYPE_ACCELERATOR[4] 정식 지원은 2017년에 나온 378 버전부터 시작됐다.[5] CUDA가 별도의 디바이스 초기화 코드 없이 디바이스 번호만 지정하면 드라이버가 알아서 기본적인 초기화를 해 주는것에 비해 OpenCL은 플랫폼(드라이버 인터페이스)를 열거후 컨텍스트를 받아온 후, 거기서 다시 디바이스를 열거 후 초기화해준 다음 커널 코드를 빌드 후 실행시키는 단계를 거쳐야 한다.[6] CUDA로 구현 가능한 것이라면 OpenCL로도 구현이 가능하며, CUDA로 구현 불가능한 것조차 OpenCL로는 구현이 가능하다. 따라서 기능만 따지자면 OpenCL이 상위호환인 셈으로, 코드가 길어지는 건 자유도가 높은 대신 그에 따른 부작용이라 보는게 좋다.