AI/ML*DL Study

✨ PyTorch와 CUDA를 이용한 GPU 메모리 관리 마스터하기: CUDA 캐싱 할당자 완벽 활용 가이드 ✨

인텔로퍼 2025. 4. 8. 00:33
반응형
PyTorch와 CUDA를 이용한 GPU 메모리 관리

 

 

PyTorch로 대규모 딥러닝 모델을 훈련하거나 대용량 데이터셋을 다루어 본 경험이 있다면, 아래와 같은 메시지가 매우 익숙할 것입니다.

torch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate 512.00 MiB. GPU 0 has a total capacity of 79.32 GiB of which 401.56 MiB is free.

바로 그 악명 높은 CUDA out of memory (OOM) 오류입니다! 😱 이 오류는 GPU가 텐서를 위한 공간을 할당하려 할 때 메모리가 부족하면 발생하며, 특히 모델을 세심하게 튜닝하고 코드를 최적화하는 데 많은 시간을 투자했을 때 마주하면 매우 답답할 수 있습니다.

 

PyTorch가 GPU 메모리 사용량을 어떻게 최적화하는지 깊이 파고들어 보고, 내부 시스템 중 일부를 조정하여 GPU 클러스터의 성능을 최대한 끌어올리는 방법을 알아보겠습니다.


🚀 GPU 메모리 관리의 중요성

기하급수적으로 증가하는 데이터셋과 점점 더 정교해지는 모델의 시대에, GPU 메모리의 효율적인 사용은 최우선 과제가 되었습니다. GPU가 아무리 강력하더라도 내장된 메모리는 항상 제한 요소입니다. 이로 인해 모든 메모리 할당 및 해제는 생각보다 더 큰 영향을 미칩니다. 연구자들이 대규모 모델을 훈련할 때 자주 겪는 몇 가지 어려움은 다음과 같습니다:

  • 💾 제한된 메모리 용량: 본질적으로 물리적인 한계입니다. 훈련 단계나 데이터가 메모리에 맞지 않으면 훈련이 진행되지 않습니다.
  • ⏳ 계산 비효율성: GPU는 데이터 전송을 기다리거나 다른 GPU가 작업을 수행하기를 기다리는 대신, 행렬 계산에 대부분의 시간을 사용해야 이상적입니다.
  • 🔗 통신 오버헤드: 통신에 소요되는 시간은 GPU가 유휴 상태이므로 낭비되는 시간입니다. 이를 최소화하려면 노드 간(느림) 및 노드 내(빠름) 통신 대역폭을 최대한 활용하고, 가급적 계산과 중첩시켜야 합니다.
  • 🧩 메모리 단편화: 잦은 메모리 할당/해제 (cudaMalloc 및 cudaFree 사용)는 GPU 메모리를 심하게 조각냅니다(단편화). 이는 심각한 문제인데, 사용 가능한 총 메모리가 상당하더라도 원하는 크기의 메모리를 할당하지 못할 수 있습니다.
  • ⏱️ 할당 오버헤드: 잦은 할당/해제는 메모리 단편화뿐만 아니라 상당한 지연 시간(latency)을 추가합니다.
  • 🌊 동적 워크로드: 다양한 배치 크기, 동적 아키텍처 또는 다양한 크기의 입력을 처리하는 모델은 런타임 시 성능 저하 없이 적응할 수 있는 유연한 메모리 관리가 필요합니다.

이러한 요인들을 이해하면 PyTorch의 **CUDA 캐싱 할당자(Caching Allocator)**가 메모리 관리 시스템에서 핵심적인 역할을 하는 이유를 알 수 있습니다. 이 할당자는 메모리 할당 오버헤드를 최소화하면서 단편화 및 지연 할당 문제를 해결합니다.

반응형

🧠 PyTorch CUDA 캐싱 할당자 이해하기

PyTorch 프로파일러를 사용하여 Llama 1B 모델을 훈련할 때 PyTorch가 메모리를 어떻게 할당하는지 살펴보겠습니다.

2단계부터 4단계까지의 메모리 사용 패턴은 매우 유사합니다.

  1. 순전파 (Forward Pass): 활성화(activation) 값이 빠르게 증가합니다.
  2. 역전파 (Backward Pass): 그래디언트(gradient)가 쌓이고, 그래디언트 계산에 사용된 저장된 활성화 값들은 점진적으로 지워집니다.
  3. 최적화 (Optimization): 모든 그래디언트가 필요한 최적화 단계를 수행하고, 다음 순전파를 시작하기 전에 옵티마이저 상태(optimizer state)를 업데이트합니다.

🤔 그런데 왜 첫 번째 단계는 후속 단계들과 매우 다르게 보일까요?

바로 이 지점에서 PyTorch의 CUDA 캐싱 할당자가 작동하기 시작합니다! 첫 번째 단계에서 할당자는 후속 단계의 속도를 높이기 위해 미리 메모리 블록을 할당하고 캐싱합니다. 이렇게 하면 후속 단계에서는 사용 가능한 메모리 블록을 다시 검색할 필요 없이 빠르게 재사용할 수 있습니다.

이 할당자는 기본적으로 PyTorch 런타임과 저수준 CUDA 드라이버 사이의 "지능적인" 중개자 역할을 수행하며, 가능한 경우 해제된 메모리 청크를 재사용하여 GPU 메모리의 할당 및 해제를 신중하게 관리합니다. 이를 통해 비용이 많이 드는 시스템 호출 (cudaMalloc, cudaFree)을 줄이고 성능을 유지합니다. 텐서가 더 이상 필요하지 않게 되면 해당 메모리는 즉시 GPU로 반환되지 않고, 나중에 재사용하기 위해 **내부 풀(pool)**에 저장됩니다. (기본적으로 메모리 풀링 기법입니다.)

🌟 이 시스템 덕분에 얻을 수 있는 몇 가지 이점:

  • 🚀 훈련 속도 향상: 캐시에서 메모리를 재사용하는 것이 매번 cudaMalloc을 호출하는 것보다 훨씬 빠릅니다.
  • 🧩 단편화 감소: 메모리 블록을 재활용하여 큰 연속 메모리 영역 확보 경쟁을 최소화하고 단편화를 줄입니다.
  • ⏱️ 지연 시간 감소: PyTorch CPU 실행은 종종 GPU 실행보다 앞서 진행되는데, 캐싱 할당자는 풀링 메커니즘을 통해 미리 메모리를 준비하여 실행 중 지연 시간을 숨기는 데 도움을 줍니다.

🛠️ CUDA 캐싱 할당자가 메모리를 관리하는 방법

이 시스템의 메모리 관리는 다음과 같은 주요 구성 요소로 나눌 수 있습니다:

1. 메모리 풀링 (Memory Pooling)

PyTorch에서 텐서를 해제할 때, 할당자는 메모리를 즉시 GPU로 반환하는 대신 캡처하여 **풀(pool)**에 저장합니다. 이 캐시된 메모리는 향후 할당 요청에 즉시 사용 가능하며, cudaMalloc 호출 오버헤드를 줄입니다.

2. 재사용 및 지연 해제 (Reuse and Delayed Freeing)

메모리 블록을 캐싱하여 요구 사항이 맞는 텐서에 즉시 재사용합니다. "지연 해제"는 메모리가 낭비되지 않도록 하며, 캐시된 블록으로 요청을 충족할 수 없을 때만 CUDA에 새 메모리를 요청합니다.

3. 단편화 완화 (Fragmentation Mitigation)

할당자는 메모리 블록을 통합하기 위해 적극적으로 작동합니다. 최근 해제된 메모리를 재사용하고 스마트 캐시를 유지 관리함으로써 단편화 가능성을 줄여 더 연속적인 메모리 공간을 확보합니다.

4. 할당 정책 균형 조정 (Balancing Allocation Policies)

크고 작은 메모리 요청을 구별하고 별도의 전략을 유지합니다. 작은 블록은 효율적으로 캐시하고, 큰 블록은 캐시 공간 독점을 방지하며 즉각적인 재사용과 가용성 간의 균형을 맞춥니다.


🎉 이것이 사용자에게 어떤 이점을 줄까요?

할당자는 훈련뿐만 아니라 추론(inference)에서도 상당한 성능 향상을 제공합니다:

  • ⚡ 더 빠른 메모리 할당: cudaMalloc 호출 감소로 할당 오버헤드가 줄어 반복 시간이 단축됩니다.
  • 💡 최적화된 리소스 사용: 메모리 효율성 증대로 메모리 제약이 심한 GPU에서도 실험이 가능해집니다.
  • 📉 단편화 감소: 지속적인 재사용/재활용으로 단편화가 억제되어 일관된 성능을 제공합니다.
  • 🧘 개발자를 위한 간소화된 워크플로우: 할당자가 내부적으로 작동하므로 개발자는 수동 메모리 관리 부담에서 벗어나 모델 개발에 더 집중할 수 있습니다.

🔧 고급 메모리 관리 기법

캐싱 할당자가 많은 일을 하지만, 다음과 같은 전략으로 메모리 사용량을 더욱 최적화할 수 있습니다:

1. 메모리 스냅샷으로 사용량 사전 모니터링 📊

GPU 메모리 사용 현황 파악이 중요합니다.

내장 함수를 사용하세요:

import torch

# 현재 할당된 GPU 메모리 (활성 텐서가 사용하는 메모리)
print(f"할당된 메모리: {torch.cuda.memory_allocated() / (1024 ** 2):.2f} MB")
# PyTorch 캐싱 할당자에 의해 예약된 총 GPU 메모리 (캐시 포함)
print(f"예약된 메모리 (캐시 포함): {torch.cuda.memory_reserved() / (1024 ** 2):.2f} MB")

PyTorch는 메모리 스냅샷 생성 및 시각화 도구를 제공하여 메모리 소비 패턴, 최대 소비 지점, 단편화, 누수 등을 파악하는 데 도움을 줍니다. 

복잡하다면 _memory_viz.py 도구로 **플레임 그래프(flame graph)**를 생성할 수도 있습니다.

2. 캐시 사전 관리 🧹

특정 경우, 예를 들어 다른 모델로 전환할 때 캐시를 명시적으로 비우고 싶을 수 있습니다.

torch.cuda.empty_cache()

⚠️ 주의: 이 함수는 캐시된 미사용 메모리만 해제합니다. 현재 사용 중인 텐서의 메모리는 해제하지 않습니다. 또한, 캐시를 비우면 다음에 큰 메모리 블록이 필요할 때 다시 cudaMalloc을 호출해야 하므로 성능 저하가 발생할 수 있습니다. 신중하게 사용해야 합니다.

3. CUDA 메모리 할당자 사용자 정의 ⚙️

PYTORCH_CUDA_ALLOC_CONF 환경 변수로 캐싱 할당자 동작(최대 캐시 크기, 할당 전략 등)을 미세 조정할 수 있습니다.

더 고급 사용자는 사용자 정의 CUDA 메모리 할당자를 통합할 수도 있습니다. (CUDA 및 PyTorch 내부 지식 필요)

  • (참고) 구현 가이드: 여기

4. 대규모 프로파일링 🔍

NVIDIA GPU 사용 시 Nsight SystemsNsight Compute 같은 도구가 유용합니다. GPU의 메모리 및 계산 처리 방식에 대한 저수준 통찰력을 제공하여 미묘한 성능 병목 현상을 식별하는 데 도움이 됩니다. 

5. 동적 할당 전략 🔮

미래에는 메모리 사용 패턴을 예측하고 리소스를 사전 할당하는 적응형 전략, CPU/GPU 통합 메모리 아키텍처 등이 중요해질 것입니다. 관련 연구 동향을 주시하세요.

6. 코드 및 훈련 최적화 (핵심 전략 요약) ✨

이전 글들에서 다룬 내용의 요약입니다.

  • 6.1) 혼합 정밀도 훈련 (Mixed Precision Training): 16비트(FP16)와 32비트(FP32) 계산을 함께 사용하여 메모리 사용량을 줄이고 속도를 높입니다. (Nvidia Apex 또는 내장 torch.amp 사용)
  • 6.2) 그래디언트 체크포인팅 (Gradient Checkpointing): 메모리를 절약하기 위해 순전파 중 일부 활성화 값만 저장하고 역전파 시 재계산합니다. 매우 깊은 네트워크에 유용합니다.
  • 6.3) 배치 크기 및 모델 아키텍처 조정: 메모리 한계에 도달하면 배치 크기를 줄이고 (필요시 그래디언트 누적(gradient accumulation) 사용), 메모리 효율적인 모델 구조를 고려합니다.
  • 6.4) 메모리 최적화 라이브러리 활용: PyTorch Lightning과 같은 라이브러리는 효율적인 데이터 로딩 등 모범 사례를 통합하여 개발 복잡성을 줄여줍니다.
  • 6.5) 제자리 연산 사용 (In-Place Operations): add_(), relu_()처럼 밑줄(_)로 끝나는 연산은 새 텐서를 생성하지 않고 입력을 직접 수정하여 메모리를 절약합니다.
    • ⚠️ 주의: 제자리 연산은 계산 그래프를 방해하고 그래디언트 계산을 복잡하게 만들 수 있어 훈련 중에는 일반적으로 권장되지 않습니다. 하지만, 그래디언트 추적이 필요 없는 추론(inference) 시나리오나 계산 그래프에서 분리된(detached) 텐서에는 유용할 수 있습니다. 코드 명확성을 위해 선별적으로 사용해야 합니다.

📝 요약

효율적인 GPU 메모리 관리는 고성능 딥러닝 시스템의 핵심입니다. 단순히 GPU를 추가하는 것보다 기존 리소스의 성능을 최적화하는 것이 중요합니다. PyTorch의 CUDA 캐싱 할당자 작동 방식을 이해하면 성능 향상을 위한 귀중한 통찰력을 얻을 수 있습니다.

핵심은 프로덕션 규모로 모델을 배포할 때 GPU 메모리 사용량을 사전에 모니터링하고 미세 조정하는 것입니다. 여기에는 코드 최적화, 환경 변수 설정, 때로는 자체 사용자 정의 메모리 할당자 구현까지 포함될 수 있습니다. 현명한 메모리 관리로 GPU의 잠재력을 최대한 발휘하세요! 💪

 

 

 

[Manus AI + Ollama] OpenManus 로컬에서 설치 및 실행해보자

Manus AI 에대해서 모른다면 요거 한번 훑고 갑시당Manus AI 에 대하여 빠르게 아라보자 Manus AI 에 대하여 빠르게 아라보자중국, 새로운 자율 AI '마누스 AI' 출시!Manus는 마음과 행동을 연결하는 일반

intelloper.tistory.com

현대 NLP - 토큰화, 임베딩, 텍스트 분류

 

현대 NLP - 토큰화, 임베딩, 텍스트 분류

주요 용어 요약NLP (자연어 처리): 컴퓨터가 인간 언어를 이해하고 생성하도록 돕는 기술LLM (대형 언어 모델): 텍스트 생성 및 이해에 사용되는 대규모 모델NLTK (Natural Language Toolkit): 텍스트 분석에

intelloper.tistory.com

DeepSeek R-1 우분투에서 써보기 feat.Ollama

 

DeepSeek R-1 우분투에서 써보기 feat.Ollama

DeepSeek에 대해서 모른다면 밑에 글 빠르게 훑어보자딥시크(DeepSeek)에 대해서 빠르게 아라보자 (요약본) 딥시크(DeepSeek)에 대해서 빠르게 아라보자 (요약본)우선 긴글을 싫어하는 당신을 위한 요

intelloper.tistory.com

 

반응형