Kinetica는 GPU를 이용한 in-memory DB로서, 주로 OLTP용보다는 OLAP용으로 사용됩니다. 아래에 간단한 TPC-H data와 query를 이용하여 일반 DBMS(여기서는 postgresql)와의 성능 비교 및 그때 CPU-GPU간의 NVLink가 어떤 효과를 내는지 nvprof로 분석해보았습니다. 아래 test는 모두 POWER9 + V100 GPU의 IBM AC922 서버로 진행했습니다.
Postgresql과 Kinetica 모두 TPC-H에서 제공되는 dbgen으로 생성한 data를 사용했습니다. 여기서 DB 크기는 10GB로 했습니다. 이렇게 하면 delimeter가 '|'로 되어 있는 SAM file들이 만들어지는데, 실제로 DB에 load를 하려면 맨 끝에 붙은 '|'은 제거해야 하더군요.
[root@localhost dbgen]# nohup ./dbgen -s 10 &
[root@localhost dbgen]# ls -ltr *.tbl
-rw-r--r--. 1 root root 14176368 Oct 23 09:35 supplier.tbl
-rw-r--r--. 1 root root 389 Oct 23 09:35 region.tbl
-rw-r--r--. 1 root root 243336157 Oct 23 09:35 part.tbl
-rw-r--r--. 1 root root 1204850769 Oct 23 09:35 partsupp.tbl
-rw-r--r--. 1 root root 1749195031 Oct 23 09:35 orders.tbl
-rw-r--r--. 1 root root 2224 Oct 23 09:35 nation.tbl
-rw-r--r--. 1 root root 7775727688 Oct 23 09:35 lineitem.tbl
-rw-r--r--. 1 root root 244847642 Oct 23 09:35 customer.tbl
Postgres에서는 다음과 같이 dbgen에 포함된 dss.ddl script를 이용하여 필요한 table들을 생성했습니다.
[root@localhost dbgen]# su - postgres
Last login: Mon Oct 22 17:27:48 KST 2018 on pts/1
-bash-4.2$ createdb dss
-bash-4.2$ psql dss
psql (9.2.23)
Type "help" for help.
dss=# \i /home/data/2.17.3/dbgen/dss.ddl
dss=# \d ORDERS
Table "public.orders"
Column | Type | Modifiers
-----------------+-----------------------+-----------
o_orderkey | integer | not null
o_custkey | integer | not null
o_orderstatus | character(1) | not null
o_totalprice | numeric(15,2) | not null
o_orderdate | date | not null
o_orderpriority | character(15) | not null
o_clerk | character(15) | not null
o_shippriority | integer | not null
o_comment | character varying(79) | not null
그 다음에 다음과 같이 SAM file에서 postgresql table 안으로 loading을 합니다. 아래는 LINEITEM의 table 예만 들었습니다.
dss=# copy lineitem from '/home/data/2.17.3/dbgen/lineitem.csv' with (FORMAT csv, DELIMITER '|');
COPY 59986052
Kinetica에도 이 SAM file을 만들고 load 하기 위해서는 다음과 같은 menu를 이용하면 됩니다. 다만 Kinetica는 GPU를 이용한 분산 DB이기 때문에, shard key를 지정하는 등 추가적인 설정 과정을 거쳐야 합니다. (여기서는 상세히 표시하지 않았습니다. 저도 제가 직접 못하고 저희 파트너사인 Unione INC의 엔지니어 도움을 받았습니다.)
먼저, TPC-H의 query들 중 1개씩의 query를 수행해보겠습니다. 이 query는 join 등이 그렇게까지 크지 않은 query라서 GPUdb가 일반 RDBMS에 비해 그다지 더 유리하지도 않은 query입니다. 그런데도, 또 별다른 tuning 없이도, Kinetica가 20배 정도 빠른 것을 보실 수 있습니다. 모두 2번씩 돌린 것이므로, postgresql에서도 data는 이미 memory에 cache된 상태라서 disk 병목은 전혀 없다고 보셔도 됩니다.
-bash-4.2$ time psql -d dss -f /home/data/RunQuery/q16.sql > /tmp/ooo
real 1m1.267s
user 0m0.374s
sys 0m0.000s
[root@localhost ~]# time /opt/gpudb/bin/kisql -host 127.0.0.1 -f /home/data/RunQuery/q16.sql > /tmp/iii
real 0m3.046s
user 0m3.729s
sys 0m0.770s
특히 Kinetica의 경우는 아래처럼 query 결과에 Query Execution Time과 Data Transfer Time을 각각 명기합니다. GPUdb에서는 query execution보다 data를 transfer하는데 더 많은 시간이 걸린다는 것을 보실 수 있습니다. 이런 것 때문에 특히 CPU와 GPU간의 연결이 CPU냐 GPU냐가 매우 중요합니다.
| Brand#54 | ECONOMY PLATED BRASS | 14 | 4 |
| Brand#55 | STANDARD BURNISHED BRASS | 14 | 4 |
+------------+-----------------------------+----------+----------------+
Rows read = 27840
Query Execution Time: 0.495 s
Data Transfer Time: 2.283 s
이 시스템에 CPU core는 32개나 있지만 GPU는 딱 2장 뿐입니다. 이럴 경우 저런 query를 10개씩 병렬로 수행하면 CPU가 더 빠를 수도 있지 않을까요 ? 꼭 그렇지는 않습니다.
여기서는 TPC-H의 16번과 19번 query 2개를 5번씩, 총 10개를 한꺼번에 수행하는 방식으로 돌리겠습니다.
Postgres에서 돌릴 script는 다음과 같습니다.
-bash-4.2$ cat 1.sh
date
echo "Starting"
psql -d dss -f /home/data/RunQuery/q16.sql &
psql -d dss -f /home/data/RunQuery/q19.sql &
...(총 10줄)
wait
date
echo "Finished"
Kinetica에서 돌릴 script도 사실상 똑같은 내용입니다.
[root@localhost ~]# cat /tmp/1.sh
date
echo "Starting"
/opt/gpudb/bin/kisql -host 127.0.0.1 -f /home/data/RunQuery/q16.sql &
/opt/gpudb/bin/kisql -host 127.0.0.1 -f /home/data/RunQuery/q19.sql &
/opt/gpudb/bin/kisql -host 127.0.0.1 -f /home/data/RunQuery/q16.sql &
/opt/gpudb/bin/kisql -host 127.0.0.1 -f /home/data/RunQuery/q19.sql &
... (총 10줄)
wait
date
echo "Finished"
각각의 수행 결과는 다음과 같습니다. Kinetica도 GPU가 2장 뿐인 환경에서도 10개의 동시 query를 잘 수행해내며, 여전히 6배 정도 빠릅니다.
Postgresql는 다음과 같습니다.
-bash-4.2$ time ./1.sh > p.txt
real 1m6.947s
user 0m1.891s
sys 0m0.055s
Kinetica는 다음과 같습니다.
[root@localhost ~]# time /tmp/1.sh > /tmp/k.txt
real 0m8.545s
user 0m29.965s
sys 0m20.525s
Kinetica에서 이 query를 처리하기 전에, nvprof를 이용해 profile data를 받기 위해서 Kinetica의 구동 script 중 일부를 수정한 뒤 restart 해주어야 합니다. 수정은 다음과 같이 간단합니다. 아래 붉은 색 부분의 명령어를 삽입해주기만 하면 됩니다.
[root@localhost ~]# vi /opt/gpudb/core/bin/gpudb
...
# nohup $HOST_MANAGER_CMD >> $HOST_MANAGER_LOG_FILENAME 2>&1 &
nohup /usr/local/cuda-9.2/bin/nvprof --log-file /tmp/nvprof/%p.txt --export-profile /tmp/nvprof/%p.nvvp --print-gpu-trace --profile-child-processes $HOST_MANAGER_CMD >> $HOST_MANAGER_LOG_FILENAME 2>&1 &
...
이렇게 얻은 profile data를 분석해보면, GPUdb의 대표적인병목인 cuda memcpy HtoD (CPU to GPU) 및 DtoH (GPU to CPU)의 사용되는 대역폭을 보실 수 있습니다. 대략 35GB/sec에 달합니다. 참고로, POWER9과 V100 GPU 사이를 연결하는 NVLink는 3개의 port를 aggregate시킨 것이라서, 이론상 단방향 대역폭이 75GB/sec (양방향 150GB/sec)에 달합니다. PCIe Gen3 버스의 이론상 단방향 대역폭이 16GB/sec(양방향 32GB/sec)에 불과하다는 것을 생각하면, Kinetica와 같은 GPUdb를 사용할 경우 CPU와 GPU의 연결은 반드시 NVLink가 되어야 한다는 것을 쉽게 아실 수 있습니다.
예의있게 광고는 클릭하고 갑니다!!
답글삭제누구신지 ??
삭제