MJay
OpenCL in GPU server 본문
OpenCL in GPU server
Cuda 랑 Nvidia 가 필요하다
GPU Server에 깔려있는 Nvidia Vendor에 관한 정보알아보기
apt-get install clinfo
첨부터 보면 Platform이 나온다. 위에 내용이 있지만 복습할 내용만 다시 정리해 봅니다.
OpenCL 프레임워크
OpenCL 애플리케이션을 개발하고 실행하기 위한 소프트웨어 시스템
플랫폼 레이어 + 런타임 + 컴파일러 로 구성되있음
호스트(host) + 하나 이상의 Device(GPU)
Vendor
AMD OpenCL, NVIDIA OpenCL , Intel OpenCL 밴더라고 합니다.
Global Memory, Constant Memory
디바이스마다 하나씩 읽어서, 해당 디바이스 안에서 접근 가능
디바이스는 컨스탄트 메모리를 읽을 수만 있음
Compute Unit
CU마라 코컬메모리가 있음
각 CU는 하나 이상의 PE로 구성되있음
PE에서 실제로 코드를 실행하면서 계산을 수행 (SPMD)
NVIDIA 경우 32개 쓰레드(warp)가 같은 명령어 실행
다수의 ALU가 하나의 명령어 실행
GPU는 여러 개의 Streaming Multiprocessor로 이루어져있다.
SM = CU
SM 안의 ALU = Processing Element(PE)
SM 안에서 warp 단위로 실행 = 워크-아이템이 32개씩 실행됨
OpenCL 설치
sudo apt update
sudo apt upgrade
sudo apt install ocl-icd-opencl-dev //include 파일을 설치한다.
sudo ln -sf /usr/local/cuda/lib64/libOpenCL.so /usr/lib/libOpenCL.so // /usr/lib로 패스를 이어준다.
sudo apt remove
Device_info
gcc -o opencl_info opencl_info.c -lOpenCL
./opencl_info
GLOBAL_MEM_SIZE = 워크 그룹당 워크-아이템 개수 제한
MAX_GROUP_SIZE - 글로벌 메모리 크기
LOCAL_MEM_SIZE - 로컬 메모리 크기
Simple Host Program
Platform- Host + 하나 이상의 Device
Device - 4개의 GPU
Context - 커널이 실행되는 환경 , 다른 객체들을 관리하기 위한 최상위 객체, 해당 컨텍스트 안에서 사용할 디바이스를 지정한다.
Command Queue - 디바이스에 커맨드(데이터 전송, 커널 실행, 딩기화)를 보내기 위해 필요
디바이스마다 커맨드-큐를 만들어주어야한다.
호스트 프로그램이 커맨드-큐에 커맨드를 넣으면, OpenCL 프레임워크의 런타이 시스템에서 이것을 빼내에 실행
프로그램 오브젝틑 만들어서 clBuildProgram을 하면 내부적으로 OpenCL 프레임워크의 OpenCL C 컴파일러를 호출한다.
OpenCL은 여러 커널로 이루어짐
디바이스에서 실행되는 코드의 기본 단위
호스트 프로그램에서 커맨드를 보ㄴ면, 여러 커널 인스턴스가 동시에 디바이스에서 실행
커널 오브젝트 - 앞에서 만든 프로그램 오브젝트에서 특정 커널 함수만 분리,
벡터 덧셈
메모리 오브젝트- 디바이스의 글로벌 메모리 혹은 컨스탄트 메모리에 공간을 할당하고 데이터를 저장하기 위해 필요
커널 함수에서 메모리 오브젝트를 인자로 받을 수 있음
디바이스 끼리 버퍼 간 복사도 할수있다.
OpenCL 커널 함수 - 같은 커널 코드를 다른 데이터 아이템에 동시에 실행한다.
하나의 워크-그룹은 하나의 CU에서 실행된다.
global_work_size 전체 워크 아이템 개수
local_worker_size 한 워크-그룹 안의 워크-아이템 개수 지정
커널 실행 인덱스 공간이 있으면 워크-그룹 단위로 CU에 할당되면 워크-아이템들이 여러 PE에서 나누어 실행된다. GPU에 비유하면 워크-그룹 단위로 SM에 할당되고 워크-아이템들이 여러 ALU 연산장치에서 나누어 실행됩니다. 총 ALU 가 32~64개 있다. 이걸 Warp라고 한다.
%s/end_time/end 치환해주는 명령어
비동기화 CL_FALSE -> 커맨드가 커맨드-큐에 enqueue되지마라 리턴
동기화 CL_TRUE -> 버퍼쓰시가 완료된 다음에 리턴
각각 버퍼쓰기, 커널 실행 , 버퍼 읽기의 시간을 측정해봤다.
- 모든 버퍼 읽기 ,쓰기를 CL_TRUE로 하고 시간을 잰다.
- KERNEL 실행은 Non-Blocking이다보니 Profiling API 함수를 사용
- Work Size는 맞게 16로 했다. (1024이 Max이다 Size에 따라 성능이 달라진다고 했는데 일다 여기서는 비슷)
- C라는 벡터에 A벡터 + B벡터 더하는 프로그램( 각각 행렬의 크기는 136840000)
- CL_FINISH는 해당 커맨드-큐의 모든 커맨드가 끝날 때까지 기다렸다 리턴
Kernel Execution 은 비동기식이라서 정확하기 위해 프로파일링 함수를 썼습니다.
결과를 보면 알수 있듯이Kernel 시간보다 메모리 옮기는 시간이 휠씬 길다.
Warp Size 가 2048일때
OpenCL error -54는 찾아보면 나오지만 Invalid Local Memory Size이다.
Max Local_Memory_Size가 1024라서 뻑이 난다.
행렬 곱셈
헷갈린 내용 한번 더 정리하고 가자
호스트 프로글매에서는 커널 함수만 호출 가능 , 데이터 병렬성을 이용한다
같은 커널 코드를 다른 아이템에 동시에 실행한다.
여러 커널 인스턴스가 각각 다른 ID를 가진다
인덱스 공간의 각 점마다 커널 인스턴스가 하나씩 동시에 실행된다.
OpenCL 버전으로 돌릴 경우
Sequential 버전으로 돌릴 경우
CPU 코어 하나만 쓰는지 끝날 기미가 안보인다.
그래서 OpenBLAS를 사용해봤다.
코어 48개를 다 쓰므로 더 빠르다
커널 실행은 Non-Blocking 이지만 하나의 Command-Queue만 가지고있고 OUT_OF_ORDER QUeue가 아니므로 순서대로 돈다. 그래도 GetProfiling 를 통해 정확히 알 수 있다.
더 구체적으로 봐보기(Profiling)
데이터 옮기는 시간보다는 확실히 더 많이 걸립니다.
Intilization이란
Reference Count -> 객체가 몇 군데서 참조되었는지 나타내 줌
Reference Count가 0이 되면 알아서 해당 객체가 저장된 메모리가 해제됨