이전 포스트에서는 토스증권 내에서 왜 자체 LLM(Large Language Model)을 개발하기로 했는지, 그리고 왜 고성능 GPU 클러스터가 필요한지 공유했는데요. 이번 포스트에서는 토스증권에서 어떻게 고성능 GPU 클러스터를 구축했는지 자세히 공유하겠습니다.
끊임없이 이동하는 데이터
어떻게 하면 고성능 GPU 클러스터를 구축할 수 있을까요? 단순히 최신의 비싼 GPU를 사서 연결하면 될까요? 아예 틀린 답은 아닙니다. 하지만, LLM 같은 매우 큰 모델은 하나의 GPU 카드에 수용할 수 없는 경우가 많습니다. 따라서 수십, 수백 개의 GPU를 연결하여 클러스터를 구축해야 됩니다.
이런 클러스터 환경에서는 학습 및 추론 시 다수의 GPU가 동시에 처리를 하는 경우가 일반적입니다. 그렇기 때문에, 데이터가 끊임없이 이동하게 되고, 이러한 ‘통신 비용’이 전체 성능에 영향을 많이 미칩니다.
이미 많은 연구[1][2][3][4]들에서 LLM 학습 시 발생하는 높은 통신 비용을 줄이려는 노력을 하고 있습니다. 최적화 알고리즘으로 절대적인 통신 양을 줄이거나, 혹은 다른 작업과 통신을 병렬로 수행함으로써 통신 시간을 오버랩하는 등 많은 방법을 제안하고 있습니다. 그러나, 이러한 소프트웨어 방법론 이외에도 하드웨어적인 방법을 통해서도 통신 비용을 최적화할 수 있습니다. 실제로는 하드웨어가 뒷받침되지 않으면 소프트웨어적인 방법론이 효과적으로 적용되지 않거나 아예 적용이 안되는 경우도 많습니다.
아마도 하드웨어에 관심이 좀 있으신 분들은 NVLink나 NVSwitch, InfiniBand 등의 용어를 들어보셨을 텐데요. 이번 포스팅에서는 이러한 것들이 무엇이고, 어떻게 통신 시간을 단축할 수 있는지, 그리고 어떻게 구성해야 하는지 공유하겠습니다.
3가지 종류의 통신 비용
LLM의 경우 학습 및 추론 시 많은 커뮤니케이션이 발생한다고 말씀드렸습니다. 좀 더 세세히 나누어 보면, 통신의 종류에는 “CPU와 GPU간의 통신”, “GPU간의 통신”, “서로 다른 GPU 서버의 GPU간의 통신” 이렇게 3가지가 있습니다.
하나씩 살펴보도록 하겠습니다.
#1 : CPU와 GPU간의 통신
CPU와 GPU 간의 통신은 빈번하지 않지만 필수적으로 발생합니다. 학습이나 추론 작업을 수행하려면 데이터를 반드시 GPU로 전송해야 하기 때문인데요. GPU가 고대역폭 메모리와 다수의 코어를 보유하고 있지만, 상대적으로 작은 용량의 휘발성 메모리만 갖고 있기 때문에 필요합니다. 예를 들어, 최신 장비인 H100도 메모리는 GPU 당 80GB가 최대입니다. 하나의 서버에 8개의 GPU를 장착하더라도 총 메모리 용량은 640GB에 불과하며, 이는 보통 수 TB에 달하는 CPU의 용량에 비해 상대적으로 작은 용량입니다.
이러한 한계로 인해 GPU는 필요할 때마다 CPU에서 데이터를 옮겨오는 식으로 운영되고 있는데요, 일반적으로 아래 그림과 같은 흐름으로 데이터를 옮기게 됩니다. 먼저 스토리지(SATA3)에 있는 데이터를 PCIe 인터페이스를 통해서 CPU의 메모리로 업로드하고, CPU 메모리에 있는 데이터를 다시 PCIe를 통해서 GPU의 메모리로 옮겨요.
이때, 각 컴포넌트의 대역폭(Bandwidth)을 살펴볼게요. 대역폭이란, 한 번에 이동할 수 있는 데이터의 양을 의미합니다. 도로를 비유해보자면, 데이터는 자동차이고 대역폭은 도로의 폭이라 할 수 있습니다. 데이터(차)가 많은데 대역폭이 낮다면(도로가 좁다면) 병목(교통체증)이 일어나겠죠? 아래 표에서 보이는 바와 같이 SATA3와 PCIe는 단순 계산 시 약 106배 정도 성능 차이가 납니다.
그렇기 때문에, 전통적인 방법으로 데이터를 GPU로 옮기게 되면 스토리지에서 병목 현상이 발생합니다. 이 병목 현상은 결코 짧지 않습니다. 예를 들어, 스토리지(SATA3)에서 GPU로 약 60GB의 데이터를 옮긴다고 하면, 60GB / (600 MB/s) = 약 100초의 시간이 걸리기 때문이죠. 물론 위의 수치는 이론상 수치라 실제로는 더 오래 걸려요.
데이터 통신을 최적화하기 위한 다양한 소프트웨어적 기법들이 현재 개발되고, 운용됩니다. 이러한 기법들은 주로 소프트웨어 기반이라 하드웨어 기반의 방법보다 비교적 적용이 용이하다는 장점이 있어요. 하지만, 하드웨어 기반의 방법 중에서도 가성비가 뛰어나고 조건만 충족한다면 기존 클러스터에도 쉽게 적용할 수 있는 하드웨어 솔루션이 있습니다.
제가 공유드리려는 하드웨어 솔루션은 NVMe(Non-Volatile Memory Express) SSD입니다. NVMe SSD는 PCIe에 바로 연결해서 사용하기 때문에, SATA3 대비 성능 병목 현상을 현저하게 줄일 수 있습니다. 그렇기 때문에 대부분의 기업, 심지어 요즘은 개인 PC에서도 대부분 선택하는 솔루션입니다. NVMe SSD를 구성하게 되면 아래 표와 같이 대역폭이 대폭 상승하게 되어 성능 병목 현상이 많이 줄어요.
물론 16개의 레인을 모두 사용하면 NVMe SSD의 성능을 64GB/s 까지도 성능을 올릴 수 있습니다만, 4개의 레인만 이용해도 충분히 높은 성능을 보여줍니다.
이렇게 NVMe SSD는 비교적 합리적인 가격으로 스토리지 병목 현상을 완화합니다. 또한, 이미 클러스터를 구축한 이후에도 메인보드의 PCIe 컨트롤러가 NVMe 호환이 된다면 바로 구성이 가능하다는 장점도 있습니다.
요약하자면,
- GPU는 상대적으로 작은 메모리를 갖고 있습니다.
- 그래서 매번 작업을 할 때마다 CPU에서 데이터를 가져와야 합니다.
- 이때, 데이터가 스토리지에 있다면 성능 병목 현상이 일어나기 쉽습니다.
- NVMe SSD는 PCIe에 바로 연결되어 데이터를 전송하므로, 스토리지 병목 현상이 많이 해소됩니다.
#2 : 같은 서버내 GPU간 통신
다음으로 LLM 학습 및 추론 시 가장 흔하게 발생하는 통신 형태인 GPU 간 통신을 자세히 살펴보겠습니다. GPU 간의 통신은 기본적으론 PCIe를 통해 주고받습니다. 하지만, PCIe는 범용 인터페이스입니다. CPU와 GPU 간 통신 및 GPU간 통신 등은 물론, 스토리지, 이더넷, IB 등의 장비도 연결될 수 있습니다. PCIe의 대역폭이 그리 낮진 않지만, 여러 장치가 연결되어 있는 특성상 대역폭을 나누어 쓰는 것이 일반적입니다. 그러다 보니, 보통 GPU 간 통신에는 최대 대역폭의 약 5~60% 정도만 활용할 수 있습니다. 이는 5세대 기준 약 50~60GB/s 로써, 다소 아쉬운 수치입니다.
NVLink는 Nvidia에서 만든, 오로지 GPU 간 통신만을 위해 고안된 GPU 전용 장치입니다. NVLink는 PCIe보다 GPU 간의 통신에서는 훨씬 더 좋은 성능을 자랑합니다. 여기서 말하는 성능이란 대역폭(Bandwidth) 뿐만 아니라 지연 시간(Latency)도 포함합니다. PCIe 5세대의 지연 시간은 100~200ns로 알려져 있습니다. 그에 반해 NVLink 4세대의 지연시간은 약 5~10ns로 10~40배 정도 짧습니다.
대역폭 또한 더욱 좋은 성능을 보여주는데요, GPU 같은 매니코어 시스템에서는 병렬 프로세싱을 주로 하기 때문에 대역폭이 지연시간 보다 월등히 중요합니다. 그렇기 때문에 Nvidia는 지속적으로 NVLink의 대역폭을 늘려왔습니다. 아래 표는 세대별 NVLink의 대역폭을 보여줍니다.
NVLink는 최적의 성능을 위해 칩셋에 내장이 되어 나오는 형태이므로 서버 구축 전에 미리 결정해야 합니다. 왜냐하면 이미 구축된 GPU 서버에는 NVLink를 추가적으로 설치할 수 없기 때문입니다.
하지만, GPU를 1대1로 직접 연결하는 장치는 추가할 수 있습니다. 이를 NVLink bridge라고 부르는데요, 아래 그림 처럼 PCIe로 이미 구성된 환경에서, 외부 공간에 그래픽카드 두 개를 잇는 브릿지 모양의 장치를 추가하여 NVLink를 통한 통신을 가능하게 해줍니다. 다만, 이는 2개의 GPU를 1대1로 연결하는 용도이기 때문에 여러 GPU를 엮지 못한다는 등의 제약 사항이 있습니다.
다시 NVLink 이야기로 돌아와 보죠. NVLink의 경우 GPU를 다이렉트로 연결하는 방식이라, 서버 구축 전 어떻게 구성할지 고려해야 하는데요, 보통은 어떤 통신 패턴에도 대응할 수 있도록 모든 GPU를 서로 연결하는, ‘all-to-all’ 형태로 구성합니다. 하지만, NVLink 4세대 기준 18개의 링크가 있다고 하더라도, 이렇게 링크를 분산하면 성능 또한 분산이 된다는 단점이 있습니다.
아래 GPU 4대가 장착된 서버를 도식화하였습니다. 편의상, 1개의 GPU 당 3개의 링크가 있다고 가정하였습니다. 이러한 상황에서 all-to-all로 연결하려면, 3개의 링크를 각각 자신을 제외한 GPU에 장착해야 합니다. 이렇게 구성하면 all-to-all 커뮤니케이션은 가능하지만, GPU간의 대역폭은 150GB/s(양방향 기준)가 아닌, 50GB/s 이 됩니다. 링크를 나누어서 서로 다른 GPU에 연결했기 때문이죠.
이러한 아쉬움을 해소하기 위해 사용되는 장치가 NVSwitch입니다. NVSwitch는 일종의 허브라고 보면 됩니다. NVLink가 GPU 간의 연결을 해주는 것이라면, NVSwitch는 NVLink 들을 연결해주는 네트워크 스위치입니다. 위의 그림을 NVSwitch를 이용해 연결한 형태가 아래 그림입니다. 보이는 바와 같이 NVLink를 GPU로 바로 연결하지 않고, 모두 NVSwitch에 연결합니다. 이제 GPU들은 더 이상 링크를 분산하지 않아도 되기 때문에, 어느 GPU와 통신을 하든 모든 링크를 활용하여 최대 대역폭으로 통신합니다.
NVSwitch도 NVLink와 마찬가지로 처음 서버 구매 시 정해야 합니다. 보통 서버 스펙에는 SXM라고 명시되어 있습니다. 예를 들어, H100의 경우, ‘H100 PCIe’ 혹은 ‘H100 SXM5’ 와 같은 식으로 통신 스타일이 명시되어 있습니다.
요약하자면,
- GPU 간 통신 방법은 PCIe와 NVLink가 있습니다.
- NVLink가 통신 성능(대역폭, 지연 시간)이 더 좋습니다.
- GPU가 더 많아지면, 그것들을 all-to-all로 연결할 NVLink도 많아져야 합니다.
- 이때, NVSwitch를 도입하면 GPU 간 NVLink를 개별적으로 연결하지 않아도 되어서 대역폭을 최대로 활용할 수 있습니다.
앞서 GPU 간의 통신을 말씀드렸는데요, LLM의 경우 최신 GPU인 H100으로 서버를 구축하더라도, 서버 한 대 만으로는 학습을 못하는 경우가 많습니다. 이러한 경우 결국 여러 대의 GPU 서버를 엮어야 하는데요, 이때 필연적으로 ‘다른 서버의 GPU’와 통신을 해야 하는 경우가 발생합니다.
앞선 두 종류의 통신보다, ‘다른 서버의 GPU간의 통신‘이 제일 비싼데요, 이러한 통신의 경우 총 5번의 데이터 복사가 일어나기 때문입니다.
예를 들어 서버 A의 GPU에서 서버 B의 GPU로 데이터가 이동(통신)한다고 생각해보죠.
순서대로,
- GPU RAM → CPU RAM으로 복사 (서버 1)
- CPU RAM → A 서버의 NIC(서버 1)
- A 서버의 NIC → B 서버의 NIC (네트워크 전송)
- B 서버의 NIC → CPU RAM (서버 2)
- CPU RAM → GPU RAM (서버 2)
이렇게 총 5번의 복사가 발생합니다.
따라서 서버 간 통신이 자주 발생하는 LLM을 위한 고성능 GPU 클러스터를 구성할 때는 이런 통신 비용을 줄여야 합니다. 이러기 위해서는 가장 먼저 데이터 복사 횟수를 줄여야 합니다. 즉, 데이터가 이동하는 경로를 최소화해 통신 비용을 감소시켜야 돼요. 또한, 현재는 PCIe 및 이더넷을 통해 통신을 하고 있으므로, 통신 대역폭을 늘려서 통신 시간 또한 단축해야 합니다.
이러한 상황에서 가장 많이 사용하는 솔루션이 인피니밴드(Infiniband)입니다. 인피니밴드를 이용하면 GPU에서 GPU로 데이터 복사를 횟수가 한 번으로 줄어요. GPU RAM에서 바로 인피니밴드 카드 (HCA)를 통한 통신으로 이러한 것들이 가능한데요. 불필요한 복사가 없어짐은 물론, 이더넷이나 PCIe 대비 아주 높은 대역폭을 갖고 있어, 통신 시간 또한 대폭 감소됩니다.
위 그림에서 보이는 바와 같이 GPU 메모리에서 PCIe 스위치를 통해 바로 HCA로 가게 되고, 여기서 인피니밴드 케이블을 통해 바로 타깃 GPU 메모리로 통신합니다.
다만, 이러한 인피니 밴드로 클러스터를 구축할 땐 몇 가지를 먼저 고려해야 하는데요, 바로 대역폭과 GPU당 인피니밴드 카드(HCA)의 개수입니다. 위의 그림에서 보이는 바와 같이 인피니밴드 카드는 GPU와 같이 PCIe 스위치에 연결된 구조입니다. 그림을 단순화하였지만, 하나의 서버에는 여러 개의 PCIe 스위치가 있고, 각 PCIe 스위치에는 또 여러 개의 슬롯이 있습니다.
만약 8개의 GPU가 장착된 서버를 2대 갖고 있고, 총 16개의 GPU를 all-to-all로 연결하고 싶다면, 아래 그림과 같이 모든 GPU에 상응하는 PCIe 스위치에 인피니밴드 카드를 장착해야 합니다. 이러면 총 16개의 인피니밴드 카드가 필요합니다.
그 후에, 각 카드에 인피니밴드 케이블(위의 그림에서 청록색 라인)을 연결한 다음, 해당 케이블들을 인피니밴드 네트워크 스위치에 연결하면 구성 완료입니다!
하지만 보통 인피니밴드 설치를 고려하는 서버들은 NVLink로 서버 내 GPU가 이미 all-to-all로 연결되어 있어서, 모든 GPU에 인피니밴드 카드를 부착하기보단 보통 2:1 구조로 2개의 GPU가 하나의 인피니밴드 카드를 공유하는 형태로 많이 구성합니다. 이는 대부분의 경우 2개의 GPU가 하나의 PCIe 스위치를 공유하기 때문이죠. 만약 4:1로 구성을 하게 된다면, 2개의 GPU는 다른 PCIe 스위치로 이동을 해야 하기 때문에 성능 저하가 일어날 수 있습니다.
다음으로, 케이블의 성능도 선택해야 하는데요. 모델의 크기나 학습의 형태에 따라 다르겠지만 개인적인 경험으로는 200 Gbps(25 GB/s)만으로도 충분하다고 생각합니다. 만약 본인의 모델이 크고 통신을 많이 한다면 400 Gbps를 선택하면 됩니다. 이 또한 모니터링을 통한 본인의 워크로드 파악이 먼저입니다. 한 가지 주의할 점은, 인피니밴드의 경우 카드는 추후 추가 장착 등 변경이 가능하지만, 인피니밴드 케이블의 경우 길이를 딱 맞춰 재단을 하는 경우가 많으므로, 처음 구축 시 신중하게 설계를 해야 합니다.
요즈음은 이더넷도 높은 성능을 보여주고 있지만 아직은 인피니밴드가 안정성이나 확장성 면에서 더 뛰어나다고 생각합니다. 하지만 본인의 상황과 업무의 특성에 맞추어 충분히 고려 후 결정하면 될 것 같습니다.
요약하자면,
- GPU에서 다른 서버의 GPU로 데이터 전송 시 일반적으로 총 5번의 데이터 복사가 이루어집니다.
- 인피니밴드를 이용해서 이 과정을 단축하고 빠르게 보낼 수 있습니다.
- 인피니밴드 구성 시, 대역폭과 인피니밴드 카드 비율을 정해야 합니다.
워크로드를 파악하는 모니터링 시스템을 구축한 후 그에 맞는 클러스터를 구축해야 합니다. 만약, LLM처럼 통신량이 많은 워크로드인 경우 스토리지 병목이 있다면 NVMe SSD를, GPU 간 통신이 많고 성능 병목이 있다면 NVLink, NVSwitch를, 서버 간 통신량이 많고 병목이 심하다면 인피니밴드를 고려해보는 것을 권장합니다.
긴 글 읽어 주셔서 감사합니다.