2018년 10월 25일 목요일

GPUdb인 Kinetica와 Postgresql의 대결

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가 되어야 한다는 것을 쉽게 아실 수 있습니다.

댓글 2개: