Metal API가 OpenGL보다 효율적인 이유
Metal API(이하 Metal)는 OpenGL과 OpenCL을 대체하기 위해 애플에서 발표한 API인데 2014년 iOS에 대한 지원을 시작으로 2015년과 2016년에는 각각 macOS와 tvOS에 대한 지원도 추가 되었습니다. 처음 Metal이 발표된 시점이 거의 3년 전이라 조금 지난 이야기이긴 합니다만 개인적으로 관심이 생겨 애플에서 Metal을 발표한 배경과 Metal이 OpenGL에 비해 효율적일 수 있는 이유에 대해 간단히 알아보았습니다.
Metal이 발표된 가장 큰 이유는 CPU와 GPU의 성능 향상 속도의 차이 때문입니다. GPU의 경우 아직까지는 병렬화에 의해 성능향상이 상대적으로 쉬운 반면, CPU는 성능향상을 위해 적용가능한 기술들이 이미 다 구현되어 있어 큰 폭의 성능 향상은 얻기가 힘든 상황입니다. 위의 그래프는 AnandTech의 OpenGL ES와 Metal의 비교 글에서 가져온 그래프인데 2010~2015년에 출시된 iPad들의 CPU와 GPU 성능이 상대적으로 얼마만큼 향상되었는지를 보여주고 있습니다. 그래프를 통해 5년 동안 6세대의 제품을 거치면서 CPU의 성능은 12배 정도 향상에 그친 반면 GPU의 성능은 180배 가량 향상되어 GPU의 성능이 향상되는 속도가 월등함을 알 수 있습니다.
CPU와 GPU의 성능이 늘어나는 속도에 차이가 점점 벌어지면서 GPU가 비효율적으로 사용되는 문제가 발생했는데 그 이유는 위의 그림과 같이 3D 그래픽 응용프로그램에서 CPU와 GPU가 pipeline을 이루어 동작하기 때문입니다. 초당 30프레임의 동작을 가정할 때 1 프레임에 해당하는 33.3ms의 시간동안 CPU는 GPU가 다음(N+1) 프레임에서 수행할 명령어를 준비하고 GPU는 현재(N) 프레임에 대해 CPU가 생성해 놓은 명령어를 처리하는 식이지요. Pipeline이 효율적으로 작동하기 위해서는 CPU와 GPU의 한 프레임당 작업 시간이 거의 비슷해야 하는데 GPU의 속도가 상대적으로 빨라지면서 CPU는 33.3ms의 시간동안 계속 바쁘게 작업하지만 GPU는 보다 빠른 시간안에 작업을 끝내고 할일이 없는 상태로 CPU를 기다리게 되는 문제가 나타나기 시작했습니다.
OpenGL을 사용하는 목적중의 하나는 한 번 작성된 코드를 여러 종류의 GPU에서 문제 없이 동작시키기 위함입니다. 따라서 OpenGL로 작성된 프로그램은 빌드 시점에 특정 GPU에 최적화된 코드를 미리 컴파일하지 않고 매 프레임마다 CPU가 GPU에 명령(draw call)을 내리기 전에 GPU상태를 체크하고(state validation) 그 결과에 따라 API의 function을 GPU 실행 바이너리로 변환하는 작업을 합니다. 상대적으로 덜 빈번하기는 하지만 필요할 경우 GPU에서 동작할 shader 프로그램을 컴파일 해야하기도 하지요(관련 링크). OpenGL의 이러한 특징은 CPU측에서 GPU 명령을 준비할 때 성능상의 부담을 주어 상대적으로 빨라진 GPU를 100% 활용하기에 충분한 양의 draw call을 만들지 못하게 합니다. 위의 그림은 2014년 WWDC에서 메탈을 소개하는 자료의(Metal Overview 세션 링크) 일부를 발췌해서 편집한 내용인데, 상단 그림은 방금 설명한 상황을 나타내고 있습니다.
반면 Metal의 경우 지원할 GPU가 정해져 있기 때문에 GPU에서 수행될 shader 프로그램을 빌드 단계에서 미리 컴파일 해 놓을 수 있고 GPU의 state validation 또한 응용프로그램이 로드될 때만 수행하도록 하여 매 프레임마다 CPU가 수행할 작업의 양을 많이 줄일 수 있습니다. 덕분에 윗 그림의 하단과 같이 OpenGL에 비해 최대 10배나 많은 draw call을 부르면서도 남는시간에 physics 연산이나 AI에 CPU 시간을 할당하여 보다 고품질의 3D 응용 프로그램을 만드는 것이 가능하다고 합니다. 아래의 슬라이드도 WWDC 자료의 일부인데 Metal을 통해 CPU에서 실행될 때 시간이 걸리는 shader 프로그램 컴파일과 state validation과 같은 작업들이 응용프로그램 빌드시점과 로드시점으로 각각 옮겨졌음을 보여주고 있습니다.
OpenGL의 경우 추상화 수준(abstraction level)이 GPU의 구조와 독립적이라 high-level AP로 분류되며, Metal은 특정 GPU에서만 수행가능한 기능을 API에 포함하고있어 low-level API로 분류됩니다. High-level API는 여러 GPU에 두루 적용될 수 있는 보편성을 얻었지만 매 프레임마다 API function을 GPU에서 실행가능한 바이너리로 변환하는 과정이 필요해 효율성의 저하를 감수해야 합니다. 반면 low-level API는 function을 GPU 실행 바이너리로 변환하는 과정이 훨씬 간단하거나 미리 컴파일된 바이너리 또는 라이브러리를 사용할 수 있어 효율적인 대신 보편성을 잃었다는 차이점이 있습니다.
CPU와 GPU의 성능 차이가 벌어지는 현상은 비단 애플 SoC에서 뿐만 아니라 PC업계에서도 나타나고 있었기 때문에 low-level API를 제안하여 CPU와 GPU의 성능차이로 인한 비효율을 해결하겠다는 접근은 애플이 Metal을 발표하기 이전부터 있던 큰 흐름이었습니다. 최근 발표된 마이크로소프트의 DirectX 12 나 Khronos그룹의 Vulcan API도 Metal과 비슷한 배경에서 제안된 low-level API이지요. 마이크로소프트의 경우 GPU를 만들지 않으면서도 low-level API를 제안한 점이 흥미로운데 AnandTech의 DirectX 12 소개 글에 따르면 GPU의 구조가 제조사에 상관없이 점점 비슷해지면서 GPU 제조사와의 긴밀한 협력을 통해 low-level API를 제안할 수 있었다고 합니다.
마지막으로 제가 가지고 있는 몇 가지 애플 기기에서 Metal과 OpenGL의 성능차이를 살펴보면서 글을 마무리하려고 하는데 고맙게도 GFXBench라는 벤치마크 프로그램은 OpenGL과 Metal로 구현된 두가지 버전을 제공하여 성능비교를 용이하게 해주고 있습니다. 아래는 순서대로 iPhone7(Apple A10), iPadAir(Apple A7), 2015 MacBook Pro 13″(Core i5-5287U)의 GFXBench 3.1 버전 수행 결과입니다.
결과를 살펴보기 전에 GFXBench의 각 테스트 항목에 대한 설명은 다음과 같습니다. 보다 자세한 내용이 궁금하실 경우 tom’sHARDWARE 사이트의 링크를 참조하여 주시기 바랍니다.
Driver Overhead 2 – GPU의 부담은 최소로, CPU에서 GPU 명령을 준비하는 부담은 최대로 하는 테스트 항목입니다. Metal이 OpenGL보다 향상된 점을 살펴보기에 가장 좋은 테스트입니다.
ALU 2 – GPU내의 프로그래머블 shader의 연산 능력을 살펴보는 테스트 입니다.
T-Rex, Manhattan – 실제 게임과 같은 환경을 모사한 테스트 입니다. 다양한 3D 효과에 대한 테스트가 포함되어 있습니다.
테스트 결과를 살펴보시면 iPad Air와 MacBook Pro의 경우는 Metal을 사용해서 성능이 향상된 효과가 확실히 보입니다. 특히 iPad Air의 “Driver Overhead 2” 항목 테스트 결과가 흥미로운데, Metal을 사용함으로서 CPU의 부하가 줄어든 효과가 상당해서 3세대 차이가 나는 A10 SoC와 비슷한 점수를 보여주고 있습니다. 다만 iPhone7의 결과는 Metal과 OpenGL의 차이가 잘 드러나지 않는데 제 생각에는 Apple A10 SoC의 CPU성능이 OpenGL을 사용하더라도 병목현상을 만들지 않을만큼 향상되었기 때문으로 추측됩니다. 벤치마크가 업데이트 되어서 CPU쪽에 좀 더 부하를 줄 수 있다면 차이가 나타날 것으로 보이는데, GFXBench의 차기 버전이 나온다면 다시 한 번 테스트를 해 보고 싶습니다.
좋은 정보 잘 보고 갑니다 ^^)b
좋은 정보 잘 보고 갑니다. 제 포스트에 참고좀 하겠습니다.
정말 좋은 정보 감사합니다.
그런데 메탈이랑 opengl이랑 비교하는게 좀 웃기다.opengl은 이제 끝이고 !적어도 dx나 벌컨정도 비교 해줬으면 의미 있을텐데 전혀 의미없어보임