결국 Wave Intrinsics는
하드웨어의 물리적 구조를 소프트웨어 알고리즘의 영역으로 끌어들여,
GPU 성능을 한계까지 끌어올린 SM6의 정점입니다.

 지난 시간, DX11(SM5)의 등장과 함께 Compute Shader와 Tessellation이 어떻게 데이터와 워크로드 모델의 패러다임을 바꿨는지 살펴보았습니다. 이는 렌더링 엔진의 책임이 커졌음을 의미하며, 동시에 GPU 프로그래밍의 주도권이 하드웨어(고정 기능)에서 소프트웨어(개발자의 설계) 로 크게 이동하는 서막이기도 했습니다. DX12는 통제권을 개발자에게 돌려준 Low-level API의 전환이었고, SM6는 Wave Intrinsics로 하드웨어의 웨이브 실행 단위를 셰이더 코드에 노출시켰습니다.

Low-Level API, Thin API

 DX11에서의 리소스 상태 관리(추적 / 검증)는 드라이버의 몫이었습니다. 드라이버가 내부에서 암묵적으로 상태를 관리하고 검증과 최적화를 대신 수행해주었습니다. 이는 필연적으로 CPU 오버헤드를 동반했습니다. DX12는 이 API 레이어를 아주 얇게(Thin) 걷어냈습니다. 이제 리소스 배리어(Resource Barrier) 설정부터 메모리 배치(Heap 기반)까지 과거 드라이버가 수행하던 저수준 제어를 개발자가 명시적으로 다룰 수 있게 된 것입니다.

Command Queue와 병렬화

 단일 컨텍스트(Immediate Context) 기반이었던 DX11은 아무리 코어가 많아도 렌더링 명령은 제한적인 경로로 GPU에 전달되어야만 했습니다. 특히 중간에 상태 변경이나 검증 비용이 누적되면 CPU 병목이 커지기 쉬웠습니다. 반면 DX12는 멀티스레드 환경에서 여러 개의 Command List를 동시에 기록하고 이를 GPU로 제출할 수 있는 구조를 택했습니다. 이는 현대 멀티코어 프로세서의 성능을 최대로 끌어내며 CPU 병목을 완화한 핵심 동력이 되었습니다.

Bindless Resource

 과거 Draw Call 오버헤드의 큰 부분은 리소스 바인딩과 그에 따른 드라이버 검증 및 상태 변경 비용이었습니다. 즉, 드로우를 호출할 때마다 텍스처·버퍼 같은 리소스를 슬롯에 바인딩하고, 드라이버가 그 상태를 확인·정리하는 작업이 반복되면서 CPU 비용이 누적되었습니다. DX12에서는 이를 Descriptor Heap Descriptor Table 이라는 거대한 메모리 풀에 리소스를 몰아넣고 인덱스만 전달하는 Bindless 방식으로 개선했습니다. 덕분에 리소스 전환 비용을 최소화하고 GPU가 연산에만 집중할 수 있는 환경을 구축했습니다.

LDS vs Wave Intrinsics

Wave Intrinsics

 SM6의 등장과 함께 추가된 기능 중 가장 혁신적인 것을 꼽으라면 단연 Wave Intrinsics입니다. 이는 GPU의 서브그룹 실행 단위(Warp 혹은 Wavefront)를 셰이더 코드에서 직접 다룰 수 있게 해줍니다. 같은 Wave 내 Lane들 사이에서 값을 교환하거나 집계(Reduction)할 수 있어서 그룹 공유 메모리(groupshared / LDS)와 배리어에 의존하던 패턴을 같은 Wave 내부에서는 더 싸게 연산할 수 있는 선택지를 제공하게 됐습니다.

  1. groupshared / LDS + 배리어 의존도
     SM5까지의 모델은 스레드 그룹 단위로 데이터를 공유할 때 보통 groupshared(LDS)에 쓰고, GroupMemoryBarrierWithGroupSync()로 동기화한 뒤 읽는 패턴이 일반적이었습니다. 이때 메모리 접근 지연과 배리어 대기 시간이 최적화의 비용으로 작동하기 쉬웠습니다.
  2. Wave 내부에서 레지스터 수준 교환 및 연산을 제공하다.
     SM6의 Wave Intrinsics는 일부 패턴에서는 이 메모리 단계를 대체하거나 줄일 수 있습니다. Wave 내부에 한해 Lane 간 값을 레지스터 수준으로 교환 및 연산할 수 있게 합니다. (단, wave 밖에서는 여전히 LDS/배리어가 필요합니다.)
    • WaveActiveSum() : Wave 내 합 (Reduction)
    • WaveReadLaneAt() : 특정 Lane 값 읽기 (Shuffle)
    • WavePrefixSum() : Prefix 연산
  3. 통제가 만든 최적화의 선택지
     결과적으로 개발자는 하드웨어가 실제로 실행하는 서브그룹 단위를 전제로 알고리즘을 설계할 수 있게 되었고, 특정 워크로드에서는 groupshared(LDS)나 배리어 비용을 줄이는 방식으로 성능을 개선할 여지가 생겼습니다. (Occlusion / Visibility 계열 처리, 타일링 / 스캔 / 리덕션 기반 작업 등)

(참고: wave 크기는 벤더와 아키텍처, 설정에 따라 달라질 수 있습니다.)

맺음말

 우리는 지금까지 DX11에서 DX12로 넘어오며, GPU의 실행 모델과 리소스와 동기화 책임이 어떻게 더 명시화되어 왔는지 살펴보았습니다. 누군가에게 DX12는 드라이버가 해주던 일을 개발자가 더 많이 떠맡게 된 불친절한 변화였을지 모릅니다. 하지만 역설적으로 이 통제권은, UE5의 거대한 두 기둥인 나나이트(Nanite)루멘(Lumen) 같은 현대적 렌더링 기술을 뒷받침하는 중요한 기반이 되었습니다.

이전글

 

GPU의 역사 - 4 : DX11(SM5), Tessellation과 Compute Shader

GPU의 역사 - 3 : DX10/SM4와 Unified Shader 전환GPU의 역사 - 2 : SIMD에서 SIMT로, Branch DivergenceGPU의 역사 - 1 : FFP에서 SIMD까지그래픽카드의 한계 2000년대 초반의 GPU는 FFP(Fixed Function Pipeline)중심이었고, 지금

chessire.tistory.com