GN⁺: 셰이더에서 mix()+step()을 사용한 조건부 이동 최적화 금지 방침
(iquilezles.org)소개
- 이 글에서는 GPU에서의 분기와 관련된 잘못된 통념을 바로잡고자 함.
- 몇몇 교육 웹사이트에서 잘못된 정보를 퍼뜨리고 있어 이를 수정하고자 함.
문제
- GPU 코드에서 조건부 실행을 구현하는 삼항 연산자를 사용하는 코드 예시를 제시함.
- 일부 사람들은 이를 산술 연산으로 대체하는 "최적화"를 제안하지만, 이는 잘못된 이해임.
- 삼항 연산자는 조건부 이동을 수행하며, 이는 간단한 비트 연산으로 구현됨.
- 실제 분기는 GPU 코드에서 발생하지만, 작은 레지스터 이동에는 사용되지 않음.
잘못된 최적화의 문제점
- 제안된 최적화는 실제로 원래 코드보다 느리게 실행됨.
-
step()
함수는 삼항 연산자로 구현되어 있어, 불필요한 곱셈과 덧셈을 추가로 수행함. - 원래 코드에서는 값이 조건부로 직접 이동됨.
기계 코드 분석
- AMD 및 Microsoft 컴파일러의 기계 코드를 통해 GPU가 분기를 수행하지 않음을 확인할 수 있음.
- 비교 연산과 비트 마스크를 사용하여 조건부 이동을 수행함.
결론
-
step()
함수를 사용한 최적화 제안은 잘못된 정보이며, 이를 바로잡아야 함. -
잘못된 정보가 20년 이상 퍼져왔으며, 이를 수정할 필요가 있음.
-
Inigo Quilez - 1994년부터 컴퓨터 그래픽스 학습 중.
Hacker News 의견
-
TFA의 결론이 맞다는 것은 확신하지만, 더 나은 버전만이 아닌 두 버전 모두의 코드 생성이 제공되면 주장이 강화될 것임
- "최적화된" 버전이 실제로 원래 버전보다 훨씬 느리게 실행된다는 점을 보여주기 위해 생성된 기계 코드를 보여주지만, 나쁜 버전이 더 나쁘다는 것을 증명하지는 않음
-
어떤 경우에 if가 실제 분기를 강제하는지 알 수 있는 좋은 방법이 있었으면 좋겠음
- 사람들이 더 비싼 mix/lerps를 사용하는 이유는 작은 오버헤드가 있을 수 있지만, 분기를 만드는 것이 두렵기 때문임
- v = x > y ? a : b;가 실제로 작동하는 것은 좋지만, if가 때로는 분기이고 때로는 아닌 구문이 있다는 것이 우려됨
-
이 기사도 관련 있음: GPU에서의 분기 작성에 대한 잘못된 조언을 바로잡음
- 과거에는 분기를 피하는 최적화가 효과적이었지만, 이제는 그렇게 하지 말아야 함
- 프로세서와 컴파일러가 변화하므로, 여러 변형을 제공하고 런타임에 가장 빠른 것을 선택하는 것이 좋음
-
컴파일러가 '최적화된' 버전이 동일하다는 것을 왜 인식하지 못하는지 궁금함
- step()을 이해하고 step()=0.0과 step()==1.0의 경우를 별도로 최적화할 수 있어야 함
-
이 문제에 걸린 적이 있음. Claude/ChatGPT도 이를 최적화로 제안하지만, 성능 저하를 초래함
- Inigo에게 감사함
-
OpenGL 함수가 GPU 기본 기능을 호출하는 대신 에뮬레이션되는지 어떻게 알 수 있는지 궁금함
-
코드 작성 시 조건부 분기가 없을 것이라는 확신을 가지기 위해 경험이 필요함
- 조건부 이후 몇 개의 연산이 분기를 유발하는지, 컴파일러가 어떤 연산을 생략할 수 있는지 알기 어려움
- 성능 테스트 수트를 사용하여 우발적인 성능 저하를 확인해야 하는지 고민됨
-
mix 함수의 변형이 벡터에 대해 작동하는 방법을 설명함