한달째 CUDA로 static Radiosity를 구현중이다.
뭐 대단한 퀄리티를 바라는건 아니고 그저 예전에 만들었던 라이팅보단 조금 더 있어보이길 바란다.
그렇기 때문에 속도가 중요했다. 조금 더 있어보이는 정도를 위해서 6박7일간 계산을 걸 수는 없으니까.
목표는 한대의 PC만 이용해서 맵 하나당 1시간 이내로 굽는것.
따라서 CUDA를 사용하는 것은 당연했다.
가장 시간을 많이 잡아먹는 부분은 역시 패치와 패치 사이를 잇는 RAY와 삼각형의 충돌처리 부분인데 처음엔 단순 BSP트리를 사용했고 약 일주일쯤 전부터 KD Tree로 변경했다.
인터넷에 돌아다니는 KT-Tree Traversal이라는 이름의 논문자료들과 거의 같은 내용이다.
트리를 변경하면서 무척 기대했다. 굉장히 빨라지지 않을까.
결과적으로 성능향상은 50%-90%수준.
일단 KD Tree Traversal 기법 자체가 탐색효율이 좋아서 그 부분에서 큰 이득을 봤다.
하지만 원했던건 탐색효율만은 아니었다.
기존 BSP트리에서 KD트리로 선회한 이유가 Stackless 트리 탐색을 사용하기 위해서였다.
왜 스택없이 탐색을 해야했느냐…하면 느리고 용량 많은 Global Memory 대신 빠르고 용량 적은 Shared Memory를 스택으로 쓰고자 했기 때문이다. 스택이 꽉 차면 Stackless 탐색이 필요하니까.
그래서 Local Memory(는 실제로는 Global Memory에 할당된다)를 스택으로 사용하는 버젼과 Shared Memory스택+Stackless 버젼을 만들었다.
오늘 드디어 테스트를 걸었는데 결과는 예상 밖이었다. 아니 사실은 절반쯤은 예상한 결과였다.
<Local Memory스택 버젼이 약간 더 빨랐다.>
Shared Memory용량 제한 때문에 Occupancy를 떨어뜨린 경우는 아예 왕창 더 느려지는 경우는 여러번 봤다.
SM2.0이전 디바이스들은 Global Memory가 캐쉬지원을 받지 못하기 때문에 Global Memory억세스를 최대한 피하는 것이 옳다.
그러나 SM2.0이상(페르미)의 디바이스들은 Global Memory에 L2캐쉬 적용이 되기 때문에 Occupancy를 떨어뜨리면서까지 Shared Memory를 사용하는 것은 다시 생각해봐야 한다.
어쨌거나 이론상 Occupancy가 같다면 Local Memory보다는 Shared Memory를 사용하는게 빠르다.
그렇지만 Occupancy를 떨어뜨리지 않는 선에서 local memory대신 shared memory를 사용해도 성능향상이 없는 경우가 있다.
이 경우는 Shared Memory사용에 따른 성능향상은 크지 않은데 비해 Shared Memory를 사용하기 위해 덧붙인 코드들 때문에 약간 더 느려진 것이다.
이번에도 그러했다.
몇 번이고 테스트 했지만 그냥 단순하게 Local Memory 스택을 사용한게 제일 빨랐다.
디바이스에 따라 차이가 있을거라고 생각한다.
최근 수개월간 주로 GTX480과 580으로 작업했기 때문에 이런 결과가 나왔는지도 모르겠다.
아마 L2캐쉬 용량이 적은 디바이스에선 Shared Memory로 구현하는 쪽이 빠르지 않을까 싶다.
비록 성능은 기대에 미치지 못했지만 Shared Memory+Stackless 방식의 잇점이 있었다.
바로 에너지 효율.
Shared Memory+Stackless방식에선 GPU Front Bus사용률이 1%정도, 온도는 76도 정도 였다.
Local Memory로 구현했을때는 FrontBus사용률 15%-20%, 80-81도.
GPU 3개쯤 꽂아서 병렬로 돌린다면 경제적,시스템의 안정성 측면으로 볼때 Shared Memory+Stackless방식이 나을지도 모르겠다.
CUDA Occupancy 계산기 사용하다가 알게 됐는데 SM3.0세대인 케플러(GTX680)는 활성화 워프 수와 레지스터 수가 두배더라. double처리에선 성능이 절반이고 SM개수도 절반이지만 동시 실행 가능한 원프 수와 레지스터 수로 상쇄가 되지 않을런지.
내 코드에선 float만 사용하니까 어쩌면 GTX680에서 더 빠르게 동작할지도 모르겠다.
GTX680으로도 테스트해 보고 싶지만 너무 비싸~