---
title: "warp divergence의 비용을 조건문 개수로 알 수 없는 이유"
author: "브레이브브라우저쓰자"
published_at: "2026-11-02T02:47:02.316Z"
updated_at: "2026-10-06T03:17:31.600Z"
tags: ["warp divergence"]
canonical_url: "https://ariser.io/@writer_a9817d5a8de0/warp-divergence%EC%9D%98-%EB%B9%84%EC%9A%A9%EC%9D%84-%EC%A1%B0%EA%B1%B4%EB%AC%B8-%EA%B0%9C%EC%88%98%EB%A1%9C-%EC%95%8C-%EC%88%98-%EC%97%86%EB%8A%94-%EC%9D%B4%EC%9C%A0"
ai_assisted: true
---

# warp divergence의 비용을 조건문 개수로 알 수 없는 이유

이 글은 AI가 활용됐습니다.

[원문](https://ariser.io/@writer_a9817d5a8de0/warp-divergence%EC%9D%98-%EB%B9%84%EC%9A%A9%EC%9D%84-%EC%A1%B0%EA%B1%B4%EB%AC%B8-%EA%B0%9C%EC%88%98%EB%A1%9C-%EC%95%8C-%EC%88%98-%EC%97%86%EB%8A%94-%EC%9D%B4%EC%9C%A0)

조건문이 많은 GPU 커널은 반드시 느릴까. 조건문 개수보다 중요한 것은 같은 warp의 thread들이 어느 경로로 갈리고 각 경로에서 얼마나 일하는가다. warp divergence의 비용을 이해하려면 분기 명령 하나를 세는 대신 실제로 사용한 lane과 명령 길이를 함께 세어야 한다.

## 경로가 갈리면 참여하지 않는 lane이 생긴다

CUDA의 SIMT 설명은 같은 warp에서 서로 다른 분기 경로를 실행할 때 해당 경로를 선택하지 않은 thread를 비활성화한다고 설명한다. 서로 다른 warp가 다른 경로를 실행하는 것과 같은 warp 안에서 경로가 갈리는 것은 다르다. [CUDA Programming Guide, SIMT Execution Model](https://docs.nvidia.com/cuda/cuda-programming-guide/03-advanced/advanced-kernel-programming.html#simt-execution-model)

숫자를 읽기 위해 자체적인 단순 모델을 만들자. warp에는 32개의 lane이 있고 각 경로는 하나씩 순서대로 발행된다. 모든 명령의 비용은 동일하고, 분기·재결합과 메모리 비용은 무시한다. 실제 GPU의 cycle 예측이나 컴파일된 명령 수를 제시하는 모델은 아니다.

16개 thread가 8개 명령의 경로 A로, 나머지 16개가 24개 명령의 경로 B로 간다면 warp는 총 32개의 경로 명령을 처리한다. 유효한 lane 연산은 16×8+16×24=512개다. 32개 명령마다 32개의 lane 자리가 있다고 세면 총 1,024개여서 유효 비율은 50%다.

## 아주 불균형한 분기가 항상 더 비싼 것은 아니다

이번에는 한 thread가 명령 하나만 수행하고 나머지 31개가 100개 명령을 수행한다고 정하자. 총 발행 길이는 101이고 유효 lane 연산은 1+31×100=3,101개다. 자리 수 32×101=3,232개와 비교하면 약 95.9%다.

분기가 매우 불균형해 보이지만 긴 경로가 많은 lane을 채우므로 앞의 반반 예보다 이 모델의 비율이 높다. 따라서 ‘분기한 thread 수가 다르다’나 ‘조건문이 있다’만으로 비용 순위를 정할 수 없다. 긴 경로를 소수만 수행하는 반대 상황이라면 결과도 달라진다.

이 비율은 유효 연산량이 다른 커널의 전체 성능을 그대로 비교하는 점수가 아니다. 두 예의 필요한 작업량 자체가 다르다. 같은 일을 하는 대안들을 비교할 때 사용한 자리와 경로 길이가 어떻게 변하는지 보아야 한다.

## 작업 배치는 warp 내부의 혼합을 바꾼다

64개의 작업 중 절반이 A, 절반이 B를 필요로 한다고 하자. 각 warp에 A·B를 16개씩 섞으면 두 warp가 각각 8+24개의 경로 명령을 처리하여 총 발행 수가 64다. A 작업 32개를 한 warp에, B 작업 32개를 다른 warp에 배치하면 각각 한 경로만 처리하므로 합은 8+24=32다. 원래 작업의 유효 연산량은 같다.

이 자체 모델에서는 동종 작업을 묶어서 경로의 빈 lane을 줄였다. 하지만 실제로 작업을 정렬하거나 큐를 재구성하는 비용은 추가된다. 순서를 바꾸며 메모리 접근이 흩어지거나 load balance가 나빠지면 이득이 줄 수도 있다. 재배치 비용까지 포함한 전체 실행 시간을 비교해야 한다.

짧은 조건문은 컴파일러가 명시적인 분기 대신 predicate를 붙인 명령으로 바꿀 수도 있다. 소스의 if 하나가 실제 분기 하나라는 가정부터 확인해야 하는 이유다. 양쪽 계산을 무조건 수행하고 결과만 고르는 변경도 공짜가 아니다. 비활성 경로의 비싼 계산이나 불필요한 메모리 접근을 늘릴 수 있다.

측정에서는 실제 instruction과 active thread의 관계, 경로별 작업량, 메모리 병목을 함께 확인한다. 개별 분기를 제거했는데 전체 시간이 그대로라면 다른 자원이 이미 처리량을 제한할 수 있다. 반대로 loop 반복 수가 thread마다 다른 경우처럼 소스에 보이는 조건문 개수보다 실행 이력의 차이가 클 수도 있다.

warp divergence를 줄이는 판단은 ‘if를 없애자’에서 끝나지 않는다. 같은 warp에 어떤 작업을 넣고, 긴 경로에서 몇 lane이 실제로 일하며, 그 변경에 어떤 비용이 붙는지 계산한다. 이 질문을 통해 분기 횟수를 세던 분석을 실행 자원의 사용 분석으로 바꿀 수 있다.

