본 포스팅은 세종대학교 컴퓨터공학과 2024년도 2학기 멀티코어프로그래밍 수업에서 진행한 프로젝트를 기반으로 작성된 것입니다.
📍프로젝트 소개
- 프로젝트 목표 : Opencl을 사용하여 CNN 모델의 GPU 가속 구현 (실행시간 단축)
- 사용 모델 : VGG16 (conv + pooling + FC)
- Data : CIFAR-10 image 3000장
- Environment : C or C++, OpenCL, Visual Studio
- code : Github
- 처음 제공받는 파일들은 아래와 같다.bin파일에는 필요한 input / weight / bias / 정확도 판단 데이터들이 들어있으며, txt 파일은 실제 정답과 내가 출력해낸 정답을 저장하는 것이다. main 파일을 실행시키면, 위의 데이터들을 통해 모델을 돌리고 값을 비교하는 전체 과정으로 구성되어 있다.
- cnn_seq는 vgg16 model의 cpp로 구성된 시퀀셜한 코드가 있으며, 이를 병렬로 처리할 수 있도록 재구성을 해야 하는 것이다.
📍프로젝트 진행
✏️ 우리 팀은 진행 단계를 크게 세 가지로 나누었다. 첫번째로는 모든 레이어에 대하여 커널 코드를 구현하고, batch 단위로 연산하는 것을 가능하게 하는 단계. 두번째로 Convolution 최적화 코드에 집중하는 단계. 마지막으로 전체 코드를 정리하는 단계로 구성하였다. 레이어별로 프로파일링을 해보았을 때 대부분의 시간이 Convolution 레이어에서 소요되기 때문에, 컨볼루션 최적화에 집중하는 것이 맞는 선택이라는 판단을 했다. 따라서 이것저것 시도를 해보았던 것 같다.
[1] 모든 레이어 커널 코드 구현 + batch 단위 연산 (1주 소요)
시퀀셜한 코드를 레이어 별로 모두 병렬로 처리 가능하게끔 kernel 부분을 만들어 GPU 매핑을 해주었다. 이후 배치 단위로 연산을 하게 했는데, 실험을 해본 결과 200장을 넘어가면 크게 성능에 변화가 없어서 200에서 멈추었다. 변화 시간은 아래와 같다.
- sec 변화 : 1697.05(seq code) → 19.80(GPU code) → 10.21(batch 추가)
[2] convolution 최적화 (2주 소요)
이후, 컨볼루션으로 해볼 수 있는 최적화 기법을 다 찾아서 해보았던 것 같다. 우리는 타일링, work size 변환, im2col, layer fusion등의 기법을 구현해서 성능 변화를 살펴보았다. 위의 배치를 추가했던 코드의 실행시간을 basic time으로 잡고 컨볼루션에 최적화 기법을 적용했을 때와의 시간과 비교를 해보았다. 결과는 아래와 같다.
[3] 최적화 정리
단계2에서 적용해보았던 것들 중 가장 성능 개선에 좋았던 기법들을 토대로 새로운 기법들도 추가하며 최종 코드를 완성해나갔다. 진행에 따른 시간 변화와 진행 과정을 간단하게 담았다.
① global id 변환 : work size를 3차원에서 2차원으로 축소를 하였다. (이게 가장 큰 이득을 보았다)
② 타일링
③ 벡터 변환 : 한 번의 명령어로 여러 값을 동시에 처리할 수 있기 때문에 계산 속도 향상에 도움이 된다.
④ zero skipping : ReLU를 거치면서 많은 0이 발생하는데, zero일 때는 컨볼루션 연산에 의미가 없다.(어차피 곱해서 더해도 0을 더하는 것이기 때문) 따라서 input이 0인 경우 스킵을 한다면 연산 시간도 줄이고, weight도 읽지 않는다면 시간적으로 이득을 볼 수 있을 것 이라고 생각했다.
📍프로젝트 후기
가장 아쉬운 점부터 말하자면, 최적화를 완벽하게 해내지 못 했다. 배치 단위로 실행을 시키면 동일한 weight을 여러 번 들고오게 되는데 이런 낭비가 없다. 이 점을 프로젝트 중에는 생각하지 못 하고 최종 발표 때 다른 팀의 발표를 듣고 깨달았다. 데이터 중복 사용 문제만 해결하면 정말 매우 매우 빨라지는 것 같다. 하지만 배운 것이 많은 프로젝트였다. 단순하게 알고리즘적으로 더 나은 방법을 모색하는 것이 아닌, 아키텍처적으로 최적화 전략을 계속해서 설계, 실험, 수정의 연속이었다. 아키텍처와 병렬처리, 데이터 이동에 대한 공부를 많이 할 수 있었던 것 같다.