본문 바로가기

Computer Science/NVIDIA

GPU란 무엇인가

안녕하세요. 진또배기입니다.

이번에는 제가 요즘 공부하는 주제인 GPU에 대해 알아보는 시간을 갖겠습니다.

출처: NVIDIA

무언가를 설명할때는 다른 사물과 비교해서 혹은 비유해서 이야기하는 것이 효과가 좋다는 것을 느낍니다.
(예를 들면, 객체지향을 설명할 때, 절차지향과 비교해서 설명하듯... 그래서 책을 읽어야하나 봅니다.)

따라서 CPU와 비교해 설명하도록 하겠습니다.

CPU: 1명의 서울대학생

GPU: 100명의 초등학생

으로 비유하면 이해가 쉬울 것 같습니다. 

사칙연산 100문제를 각각에게 내주면 누가 더 빠를까요? 당연히 100명의 초등학생이 빠를 것입니다. 하지만 미적분 1문제를 각각에게 내주면 100명의 초등학생은 10년이 지나야 풀 수 있겠고 1명의 서울대생이 빠르게 문제를 풀 수 있을 것입니다.

이처럼 CPU는 복잡한 연산을 빠르게 처리하도록 설계되었고, 반면 GPU는 단순하지만 많은 연산을 빠르게 수행하도록 설계되었습니다. CPU는 처리시간을 빠르게 하는 쪽으로 발전했고 GPU는 처리량을 많게 하는 쪽으로 발전했습니다.

GPU의 사전적 의미는 다음과 같습니다.

GPU(Graphics Processing Unit): 컴퓨터 시스템에서, 그래픽 연산을 빠르게 처리하여 결과값을 모니터에 출력하는 연산 장치

CPU는 고성능 프로세서가 몇개정도 들어가 있지만 GPU는 성능이 그리 높지 않은 프로세서가 수천개나 들어있습니다. 

GPU에는 왜 이렇게 많은 프로세서가 들어가 있을까요?

모니터에서 보여지는 이미지는 자세히 보면 많은 점들로 이루어져 있습니다. 이 점 하나를 픽셀이라고합니다. 지금 많이 사용하는 HD해상도의 크기는 가로가 1920개, 세로는 1080개의 픽셀로 이루어져 있습니다. 그래서 가로와 세로의 픽셀 수를 곱하면 약 200만개의 픽셀이 모니터에 표시되고 있습니다.

동영상은 이미지 한장 한장을 빠르게 표시해, 마치 움직이는 것처럼 보이게 하는 것인데요. 보통 동영상은 1초에 30장의 이미지를 사용합니다. 이때, 사용하는 한장의 이미지를 프레임이라고 합니다. 

한장의 프레임에는 200만개 종류의 픽셀이 들어있고, 1초면 30장의 프레임을 계산해야하니, 컴퓨터가 1초에 계산해야하는 픽셀의 수는 약 6천만개가 됩니다.

1,920 X 1,080 X 30 = 62,208,000 (만약 10초면 6억개....?)

게임 같은 경우에는 1초에 60프레임을 계산해주는 경우도 많고 3차원 모델을 실시간으로 보여 줘야되니 계산은 더 어려워집니다.
이처럼 gpu 는 모니터에 뿌려질 픽셀의 정보를 계산해 줍니다. 픽셀의 정보를 cpu 를 이용해서 계산해 수도 있지만 그것은 효율적인 방법이 아닙니다.

마치 서울대생에게 산수 문제 백개를 풀게 하는 것과 같습니다. 단순한 계산을 하느라고 더 중요한 정보를 처리하지 못할 겁니다. 그래픽은 게임과 함께 발전했다고 해도 과언이 아닐텐데요. 반도체 기술의 발전과 함께 게임 그래픽도 많은 발전을 이루었습니다.

GPU의 작동방식

요즘 나오는 게임은 3차원 모델로 만들어 주는데요 GPU의 작동 방식을 알아보기 위해서 3d 그래픽이 어떻게 표현 되는지 알아보겠습니다.

3차원 모델은 점,선,면으로 이루어져있습니다. 점과 점을 연결하면 선이 되고 이 선을 채워주면 면이 됩니다. 가장 기본이 되는게 점이 되어 이 점을 정점이라고 합니다. 이런 정점들을 서로 연결해서 만든 3차 모델을 폴리곤 매쉬 라고 합니다. 폴리곤 이라고 하는 것은 다각형이라고 해석할 수 있고 매쉬 라는 것은 그물이라는 의미이니까 해석하자면 다각형 그물 상태라고 할수 있습니다. 보통 폴리곤 은 가장 간단한 형태의 다각형인 삼각형을 이용하는데 삼각형을 이용해 그물처럼 서로 연결해 가면서 3차원 모델을 만들어간다고 이해하시면 될 것 같습니다

이 삼각형을 만들기 위해서는 기본적으로 3개의 점이 있어야 합니다. 각각의 정점들은 위치를 표시할 수 있는 좌표 값을 가지고 있습니다. 3차원 이기 때문에 x축 y축 z축 3개의 위치 값이 필요합니다. 이 점들을 이용해 3차원 모델을 표현하기 위해서 각 점들의 수직 방향으로 표현되는 노말이라는 정보도 필요하게 됩니다.(normal, 법선 벡터라고 이해하시면 될 것 같습니다.) 노말은 정점이 어느 방향을 향하는 지 알기 위해서 필요한 정보입니다.
일반적으로 3차원 모델의 바깥쪽으로 노말의 값을 표현합니다.

이 외에도 3차원 모델의 색을 입혀주기 위해서 각 정점의 이미지에 대한 정보가 필요합니다. 이밖에 많은 정보들이 정점의 데이터에 저장됩니다.

정점 데이터를 GPU에 입력하면 입력받은 정보에 대해서 연산을 수행합니다. 입력받은 정점데이터는 GPU프로그램을 통해서 삼각형으로 만들어 줍니다. 이 삼각형은 결국에 모니터에 픽셀로 표시되어야 합니다. 그래서 삼각형은 픽셀로 채워주기 위해 픽셀의 구역을 나눠 줍니다. 이때 만들어진 예비 픽셀을 Fragment라고 합니다. 정점 데이터에서 받은 노말 값의 평균을 이용해서 이 조각들의 노말 값도 계산해 줍니다. 그리고 이 조각의 색을 입혀 주기위해 이미지에 대한 정보를 조각들마다 계산해줍니다. 그 다음에 빛을 이용한 명암에 차이도 표현해줍니다. 그리고 깊이 정보를 이용해서 뒤에 있는 물체가 가리게되면 그 픽셀은 표현 되지 않도록 계산해줍니다. 3차원정보 라고 해도 결국 모니터는 2차원이기 때문에 이런 과정이 필요합니다. 이런 과정을 거쳐서 프래그먼트 조각들은 최종 픽셀로 만들어지고 모니터에 표현 됩니다. 그래서 정점데이터에는 정점의 위치에 대한 정보와 노말에 대한 정보, 이미지 좌표에 대한 정보, 깊이에 대한 정보 등 많은 정보들이 저장되어 있습니다. 또 3차원 모델은 계속해서 움직이게 됩니다. 위치가 변하고 크기가 변하고 회전도 합니다. 그렇기 때문에 변화는 정점의 데이터를 계속해서 계산해 줘야 합니다.

1) 정점데이터 입력
2) 정점연산
3) 삼각형 생성
4) 조각화
5) 픽셀화
6) 이미지화
7) 출력

이런 연산은 행렬의 곱셈 형태로 진행됩니다. 수십 만 개의 달하는 정점데이터를 행렬의 곱셈 형태로 실시간 계산해주고 모니터에 뿌려줄 픽셀을 만들어줘야 합니다. 이것이 GPU의 역할입니다.

1개의 Fragment는 명령어에 의해 처리됩니다. 하나의 Fragment는 코어 안에 있는 산술 논리 장치인 ALU에서 처리됩니다. 두 개의 코어를 사용하면 두 개의 Fragment가 동시에 계산됩니다. 코어의 개수를 늘리게 되면 더 많은 Fragment를 한 번에 계산할수 있겠죠. 코어에 들어있는 ALU의 대수를 8개로 늘리면 한 번에 8개의 연산을 수행할 수 있습니다. 하나의 명령어로 8개의 Fragment가 한꺼번에 계산된다는 것입니다.

이처럼 하나의 명령어로 여러 개의 데이터를 얻어 내는 방식을 SIMD라고합니다.(Single Instruction Multiple Data)
코어가 많을수록 더 많은 Fragment가 한 번에 계산 되겠죠.

Fragment뿐만 아니고 픽셀을 만들기 위한 모든 정보들이 이런 방식으로 처리됩니다. 그래픽의 연산은 각각의 정점 이나 픽셀마다 독립적으로 계산됩니다. 옆에 있는 픽셀 값이 계산되길 기다렸다가 부유 다음 픽셀 값이 계산될 필요가 없습니다. 

예를 들어, A+B=C이고, C+1=D일 때, 
D값을 구하기 위해서는 C값이 먼저 계산되어야합니다. 그래서 A+B=C와 C+1=D를 따로 계산할 수 없고 앞이 먼저 계산되고 뒤로 순차적으로 계산 되어야만 합니다.

하지만 그래픽의 처리에서 정점이나 Fragment픽셀들은 순차적으로 계산될 필요가 없고 서로 독립적으로 계산됩니다. CPU는 순차적인 연산에는 효율적이지 못합니다. 하나의 결과값을 기다렸다가 다음 결과 값을 얻어 내려면 하나의 코어만 사용됩니다. 그러면 나머지 코어들은 놀게 되고 효율이 많이 떨어지게 됩니다. 그래서 서로 독립적인 대량의 데이터를 한 번에 처리할 때 GPU 가 효과적입니다.

그래서 이런 GPU의 특징을 활용해 대량의 데이터를 처리하고자 하는 욕구가 생기기 시작했는데요. 하지만 GPU는 그래픽 처리를 목적으로 만들어졌기 때문에 데이터의 형식을 그래픽이 처리되는 형태로 바꿔줘야 하는 번거로움이 있었습니다. 많은 데이터를 행렬의 곱셈 방식으로 처리하는 것은 일반적인 계산에서는 적합하지 않을 때가 많습니다


그래서 이런 사람들의 불편함을 해결하고자 CUDA OpenCL 같은 프로그램이 개발되었습니다.

CUDA는 NVIDIA사 에서 제공하는 GPU프로그램입니다.CUDA 코어와 프로그램이 데이터를 처리하는 방식에 대해서 알아보겠습니다.

CUDA Core의 데이터 처리 방식

스레드를 코어에 대응

여기에 GPU가 처리 해야 할 100개의 작업이 있습니다. 코어가 처리해야 할 하나의 작업을 스레드라고 하면 서로 독립적인 100개의 스레드를 처리할 가장 이상적인 방법은 100개에 코어에 각각 하나씩 할당하는 SIMD방법입니다. 하지만 GPU가 처리할 스레드는 너무나 많습니다. 백만 개의 스레드를 처리하기 위해 백만 개의 코어를 이용하면 좋겠지만 이것은 현실적으로 불가능합니다. 현재의 GPU에 들어있는 코어의 갯수는 몇 천개에 불과합니다. 그래서 GPU는 SIMT라는 방식으로 작업을 처리합니다. 이것은 스레드 중심의 처리방식입니다. (Single Instruction Multiple Threading)

(*스레드(thread)란 프로세스(process) 내에서 실제로 작업을 수행하는 주체를 의미합니다.)

100만개의 스레드를 1000개의 코어에서 계산한다고 생각하면, 100만개의 스레드를 각각 천개의 코어 중에 어디론가 보내야합니다. --> 100만 : 1000 대응

분배하는 과정이 뭔가 좀 복잡하게 느껴지죠.

코어를 100개씩 10개의 그룹으로 묶어주면 어떨까요? 100만개의 스레드를 10개의 그룹 어디론가 보내면 되니까 분배가 조금은 쉬워진 것 같습니다. -->100만 : 10 대응

이번에는 스레드를 천개씩 묶어서 천개의 묶음으로 만들어 줍니다. 천개의 묶음을 10개의 그룹 중 어디론가 보내면 되니까 분배가 조금은 더 쉬워지게 됩니다. -->1000:10 대응

이렇게 되면 하나의 코어 그룹에서 처리되는 스레드는 1000개인데 코어는 100개 밖에 없기 때문에 한번에 처리 될 수는 없습니다.

그래서 이 스레드를 100개씩 묶어주면 다시 10개 그룹이 생성되는데 이러한 스레드 그룹을 워프(Warp)라고 합니다. 하나의 워프안에는 100개의 스레드가 들어있기 때문에 하나의 스레드가 각각 하나의 코어에 할당 되어서 연산을 수행할 수 있습니다. 그래서 하나의 워프 안에 들어있는 스레드들은 마치 하나의 작업처럼 처리됩니다.

RTX 3090 GPU에는 만개가 넘는 코어가 들어 있습니다. 코어는 128개씩 그룹으로 묶여있습니다. 128개의 스레드가 들어있는 워프들이 코어 그룹 안에서 연산됩니다.  워프안에는 128개의 스레드가 들어있고 코어그룹 안에는 128개의 코어 들어있기 때문에 하나의 스레드하나의 코어에 할당되어 연산이 수행 되게 됩니다. 

코어가 연산을 수행하다가 메모리로부터 데이터를 읽어오기 위해서 잠시 대기하는 시간이 생기게 됩니다. 이것을 지연 시간 이라고 하는데, CPU같은 경우에 지연시간을 최대한 줄이기 위해서 코어 근처에 캐쉬 메모리를 만드는 방법을 사용합니다. 

반면 GPU같은 경우 코어 그룹에서 하나의 어플을 수행하다가 지연시간이 발생하면 바로 다음 워프를 수행하게 됩니다. 이 워프를 수행하다가 지연시간이 발생하면 다시 다음 워프를 수행함으로써 지연시간이 발생하지 않고 계속해서 연산이 이루어지게 됩니다. GPU는 코어가 쉴 틈 없이 계속해서 연산을 수행하는셈인데 지연시간을 없게 하려면 그만큼 스레드 많이 있어야 효율이 좋습니다. 이와 같은 방식으로 GPU는 대량의 데이터를 병렬처리 하는데 있어 효율이 좋다고 할 수 있습니다. 딥런닝 에서는 신경 세포를 흉내내는 신경망 네트워크를 사용합니다. 많은 데이터가 입력되고 그것이 연산되어서 결과값을 도출해내는 형태입니다. 이것은 곱하기 와 더하기를 반복하는 구조입니다. 많은 양의 데이터를 이용한 단순한 계산이므로 CPU를 활용하면 효과적입니다.

비트코인의 채굴 방법은 정해진 해쉬 값이 나올 때까지 임의의 숫자를 계속해서 더하는 방식입니다. 이것은 순차적으로 진행될 필요가 없고 더하기를 반복하는 단순 작업이기 때문에 GPU를 사용하여 계산하고 있습니다.

날씨예보에 사용되는 컴퓨터는 물이나 공기 같은 유체의 흐름을 계산해야 합니다. 특정 지역의 온도 습도 공기의 흐름을 격자로 나누어 날씨의 변화를 계산해줍니다. 격자를 작게하면 그만큼 컴퓨터의 능력이 요구되지만 병렬도는 증가해서 더 많은 스레드를 이용한 병렬계산을 할 수 있습니다.

 


제가 준비한 내용은 여기까지입니다