메모리 접근 패턴
개요
메모리 접근 패턴(memory access pattern)은 프로그램이 메모리 주소를 읽고 쓰는 방식을 나타낸다. 접근 패턴은 캐시 성능, SIMD/SIMT 유닛 활용도, 메모리 대역폭 사용량에 직접적인 영향을 미치며, 동일한 알고리즘도 접근 패턴에 따라 수배 차이의 성능 차이를 보인다.
GPU 프로그래밍에서 메모리 접근 패턴은 특히 중요하다. NVIDIA GPU의 워프(warp)는 32개 스레드가 SIMT 방식으로 실행되는데, 32개 스레드가 한 번에 접근하는 메모리 주소가 연속적이면 하나의 메모리 트랜잭션으로 처리되지만, 불연속적이면 여러 트랜잭션이 필요해 성능이 저하된다. 딥 러닝 워크로드의 경우, 컨볼루션·행렬 곱셈·어텐션 연산 등에서 메모리 접근 패턴을 최적화하는 것이 전체 파이프라인 성능의 핵심이다.
핵심 개념
접근 패턴 분류
| 패턴 유형 | 설명 | 대표 사용처 | 캐시 활용도 |
|---|---|---|---|
| Sequential (연속) | 주소가 1씩 증가/감소하며 접근 | 배열 순회, 벡터 연산 | 매우 높음 |
| Strided (보폭) | 고정된 간격으로 건너뛰며 접근 | 행렬 전치, 2D 배열 순회 | 높음 (보폭 크기에 따라 달라짐) |
| Random (랜덤) | 예측 불가능한 주소에 접근 | 해시 테이블, 그래프 순회 | 매우 낮음 |
| Gather (수집) | 랜덤 주소에서 읽기, 연속 쓰기 | 인덱스 배열 기반 읽기 | 읽기는 낮음, 쓰기는 높음 |
| Scatter (산재) | 연속 읽기, 랜덤 주소에 쓰기 | 역인덱싱, 스트리밍 쓰기 | 읽기는 높음, 쓰기는 낮음 |
캐시 라인과 접근 패턴의 관계
현대 CPU/GPU의 캐시 라인은 보통 64바이트이다. 하나의 캐시 라인에 들어가는 데이터 양은 접근 패턴의 효율성을 결정한다.
- Sequential 접근: 첫 번째 캐시 미스 후 연속된 데이터가 캐시에 적재되어 후속 접근은 히트
- Strided 접근: 보폭이 캐시 라인 크기 이내이면 유효, 크면 매 접근마다 미스 발생
- Random 접근: 매 접근마다 캐시 미스 발생 가능성 높음
코일레싱(Coalescing) — GPU 관점
GPU에서 코일레싱은 워프 내 32개 스레드의 메모리 접근을 하나의 트랜잭션으로 묶는 것이다.
코일레싱 조건 (NVIDIA GPU):
- 32개 스레드가 접근하는 주소가 128바이트 정렬 범위 내에 있어야 함
- 주소 순서가 스레드 ID와 일치하면 최적의 코일레싱 달성
- 스트라이드가 1인 접근(연속 접근)이 가장 효과적
코일레싱 실패 시 결과:
- 미코일레싱(non-coalesced) 접근 시 여러 메모리 트랜잭션 발생
- 메모리 대역폭 낭비 (최대 32배)
- 워프 실행 시간 증가
비교/분석
접근 패턴별 성능 비교
| 패턴 | GPU 코일레싱 | 캐시 적중률 | 대역폭 활용 | 병렬화 난이도 |
|---|---|---|---|---|
| Sequential | 최적 | 매우 높음 | 최대 | 쉬움 |
| Strided (소) | 양호 | 높음 | 높음 | 쉬움 |
| Strided (대) | 불량 | 낮음 | 낮음 | 보통 |
| Gather | 불량 | 낮음 | 낮음 | 보통 |
| Scatter | 불량 | 낮음 | 낮음 | 어려움 |
| Random | 매우 불량 | 매우 낮음 | 매우 낮음 | 매우 어려움 |
AI 워크로드에서의 접근 패턴
| 연산 유형 | 주요 접근 패턴 | 최적화 기법 |
|---|---|---|
| 행렬 곱셈 (GEMM) | Sequential + Strided | 타일링, 전치 |
| 컨볼루션 | Strided (2D) | im2col, 직접 컨볼루션 |
| 어텐션 | Random (토큰 간) | Flash Attention, KV 캐시 |
| 임베딩 룩업 | Gather | 임베딩 테이블 재배치 |
| 소프트맥스 | Sequential | 온라인 합산 최적화 |
| 배치 정규화 | Sequential | 커널 융합 |
동작 원리
메모리 접근 패턴 최적화 기법
1. 타일링(Tiling)
큰 데이터 블록을 작은 타일로 분할하여 캐시에 맞게 처리한다.
원본: A[M×N] × B[N×P] = C[M×P]
타일링 후:
for ii in range(0, M, TILE_M):
for jj in range(0, P, TILE_P):
for kk in range(0, N, TILE_K):
# TILE_M × TILE_K × TILE_K × TILE_P 크기의 타일 곱셈
# 캐시 라인에 데이터가 재사용됨
2. 데이터 전치(Transpose)
행렬 전치를 통해 스트라이드 접근을 연속 접근으로 변환한다.
A[M×N] 접근 패턴: A[i][j] → stride = N (비연속)
A^T[N×M] 접근 패턴: A^T[j][i] → stride = 1 (연속)
3. 패딩(Padding)
뱅크 충돌이나 캐시 충돌을 방지하기 위해 배열에 패딩을 추가한다.
A[M][N] → A[M][N + PAD] # PAD = 캐시 라인 크기 기준
4. 소프트웨어 프리페치
순방향 접근 패턴에서 미리 데이터를 프리페치하여 메모리 지연시간을 숨긴다.
__builtin_prefetch(&data[i + PREFETCH_DISTANCE], 0, 1);
GPU 메모리 접근 파이프라인
- 스케줄러: 워프를 선택하고 실행 준비
- 주소 생성: 각 스레드의 접근 주소 계산
- 주소 정렬: 코일레싱을 위한 주소 정렬
- 트랜잭션 분할: 미코일레싱 시 여러 트랜잭션으로 분할
- 캐시/DRAM 접근: 캐시 �트 시 빠른 반환, 미스 시 DRAM 접근
- 데이터 반환: 스레드에 데이터 전달
장단점
장점
- 올바른 접근 패턴 선택으로 캐시 적중률 극대화 가능
- SIMD/SIMT 유닛의 병렬 연산 효율 극대화
- 메모리 대역폭 최대 활용으로 연산 병목 완화
- 전력 소비 절감 (불필요한 메모리 접근 감소)
단점
- 접근 패턴 최적화를 위한 코드 복잡도 증가
- 데이터 재구성(전치, 타일링)에 오버헤드 발생
- 일부 알고리즘은 접근 패턴 최적화가 어려움 (그래프, 트리 등)
- 하드웨어 아키텍처에 따라 최적 패턴이 다를 수 있음
관련 기술
- 메모리 레이아웃 최적화 — NHWC/NCHW 레이아웃 선택과 접근 패턴의 연관성
- 뱅크 충돌 방지 — Shared Memory 뱅크 충돌 회피 기법
- Square Tiling 기초 — GEMM 타일링을 통한 접근 패턴 최적화
- Register/Shared Memory Tiling — 메모리 계층별 타일링 전략
참고 자료
- NVIDIA CUDA Programming Guide — Memory Access Patterns
- "Data Movement Is All You Need" (MLSys 2021)
- Hennessy & Patterson, Computer Architecture: A Quantitative Approach, 6th Edition
- GPU Gems 2, Chapter 31: Order-Independent Transparency
핵심 정리
메모리 접근 패턴은 프로그램이 메모리 주소를 순서대로, 간격을 두고, 혹은 무작위로 접근하는 방식을 나타내며, 캐시 성능과 SIMD/SIMT 활용도에 직접적인 영향을 미친다. GPU에서는 워프 단위 코일레싱이 핵심으로, 연속적인 접근 패턴이 가장 효율적이다. AI 워크로드에서는 타일링, 전치, 패딩 등의 기법을 통해 불리한 접근 패턴을 최적화할 수 있다. 적절한 접근 패턴 선택은 성능을 수배 향상시킬 수 있으며, 하드웨어 아키텍처와 워크로드 특성을 이해한 기반 위에서 이루어져야 한다.