프린트 하기

OS환경 : Oracle Linux 6.8 (64bit)


DB 환경 : Oracle Database 11.2.0.4


방법 : linux7.6에 PostgreSQL 10 + PG-Strom 2.2 GPU 사용 테스트

이어지는글 : linux7.6에 PostgreSQL 10 + PG-Strom 2.2 구성하기(성공)



GPU 사용 테스트

GPU를 사용하는지 테스트 해보기 위해 샘플테이블 3개 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# su - postgres
$ psql
psql> 
drop table t0;
drop table t1;
drop table t2;
CREATE TABLE t0 AS 
    SELECT
        gs as cat,
        gs as ax,
        gs as aid,
        gs as bid,
        'imsidata' || gs AS test_string,
        md5(random()::text) AS random_string
    FROM
        generate_series(1100000) AS gs;
 
CREATE TABLE t1 AS 
    SELECT
        gs as cat,
        gs as ax,
        gs as aid,
        gs as bid,
        'imsidata' || gs AS test_string,
        md5(random()::text) AS random_string
    FROM
        generate_series(1100000) AS gs;
 
CREATE TABLE t2 AS 
    SELECT
        gs as cat,
        gs as ax,
        gs as aid,
        gs as bid,
        'imsidata' || gs AS test_string,
        md5(random()::text) AS random_string
    FROM
        generate_series(1100000) AS gs;



EXPLAIN 명령을 사용해 실행계획 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
psql>
# EXPLAIN SELECT cat,count(*),avg(ax)
                     FROM t0 NATURAL JOIN t1 NATURAL JOIN t2
                    GROUP BY cat;
                                  QUERY PLAN
--------------------------------------------------------------------------------
 GroupAggregate  (cost=989186.82..989190.94 rows=27 width=20)
   Group Key: t0.cat
   ->  Sort  (cost=989186.82..989187.29 rows=189 width=44)
         Sort Key: t0.cat
         ->  Custom Scan (GpuPreAgg)  (cost=989175.89..989179.67 rows=189 width=44)
               Reduction: Local
               GPU Projection: cat, pgstrom.nrows(), pgstrom.nrows((ax IS NOT NULL)), pgstrom.psum(ax)
               Combined GpuJoin: enabled
               ->  Custom Scan (GpuJoin) on t0  (cost=14744.40..875804.46 rows=99996736 width=12)
                     GPU Projection: t0.cat, t1.ax
                     Outer Scan: t0  (cost=0.00..1833360.36 rows=99996736 width=12)
                     Depth 1: GpuHashJoin  (nrows 99996736...99996736)
                              HashKeys: t0.aid
                              JoinQuals: (t0.aid = t1.aid)
                              KDS-Hash (size: 10.39MB)
                     Depth 2: GpuHashJoin  (nrows 99996736...99996736)
                              HashKeys: t0.bid
                              JoinQuals: (t0.bid = t2.bid)
                              KDS-Hash (size: 10.78MB)
                     ->  Seq Scan on t1  (cost=0.00..1972.85 rows=103785 width=12)
                     ->  Seq Scan on t2  (cost=0.00..1935.00 rows=100000 width=4)
(21 rows)

실행 계획 속에 익숙하지 않은 구문이 포함되어있음(GPU Projection 등)

CustomScan 매커니즘을 이용해서 GpuJoin 및 GpuPreAgg이 구현되어 있음

여기서 GpuJoin은 t0과 t1 및 t2와 JOIN 작업을 수행하고 그 결과를 GpuPreAgg가 받은 뒤 

GPU에서 cat 컬럼으로 GROUP BY를 실행함


PostgreSQL가 쿼리 실행 계획을 구축하는 과정에서 PG-Strom는 최적화에 개입하고 

SCAN, JOIN, GROUP BY의 각 워크로드를 GPU에서 실행 가능한 경우 그 비용(Cost)을 산출하여 

PostgreSQL의 최적화 실행 계획 후보를 제시함

추정 된 비용(Cost) 값이 CPU에서 실행되는 실행 계획보다 작은 값인 경우, GPU를 이용한 대체 실행 계획이 선택됨


GPU 실행을 위해서는 사용중인 연산자, 기능 및 데이터 유형이 PG-Strom에서 지원되어야함

int 나 float 같은 수치 형, date 또는 timestamp 같은 시각 형, text와 같은 문자열이 지원되고 있으며, 

사칙 연산과 대소 비교 등 다양한 내장 연산자가 지원됨 전체 목록은 레퍼런스를 참조해야함



아래 내용부터는 데이터가 1억건 이상이라 참고용으로만 보기 바람

http://heterodb.github.io/pg-strom/operations/ 내용을 그대로 가져와서 

한글 문장이 어색한 부분만 수정한 내용임



CPU + GPU 하이브리드 병렬

PG-Strom은 PostgreSQL의 CPU 병렬 실행도 지원함

CPU 병렬 실행 모드에서 Gather 노드는 여러 백그라운드 워커 프로세스를 시작한 다음 

개별 백그라운드 워커에 의한 "부분"실행 결과를 수집함

GpuJoin 또는 GpuPreAgg와 같은 PG-Strom에서 제공하는 CustomScan 실행 계획은 백그라운드에서의 실행을 지원함 

GPU를 사용하여 부분 작업을 개별적으로 처리함 

CPU 코어는 일반적으로 GPU에서 SQL 워크로드를 실행하는 것보다 GPU에 데이터를 제공하기 위해 

버퍼를 설정하는 데 훨씬 더 많은 시간이 필요하므로 CPU와 GPU 병렬의 하이브리드 사용은 더 높은 성능을 기대할 수 있음 반면에 각 프로세스는 GPU와 통신하는 데 필요한 CUDA 컨텍스트를 생성하고 특정 양의 GPU 리소스를 소비하므로 CPU 측의 병렬 처리가 항상 좋은 것은 아님


아래의 쿼리 실행 계획을 보면 수집 아래의 실행 계획 트리는 백그라운드 작업자 프로세스에서 실행 가능합니다. t04 개의 백그라운드 작업자 프로세스와 코디네이터 프로세스를 사용하여 1 억 개의 행이있는 테이블을 스캔 합니다. 즉, GpuJoin 및 GpuPreAgg에 의해 프로세스 당 2 천만 개의 행이 처리 된 후 결과가 Gather 노드에서 병합됩니다.


아래 실행 계획을 보면 Gather 다음 실행 계획은 백그라운드에서 실행됨

4 개의 백그라운드 작업자 프로세스와 코디네이터 프로세스를 사용하여 1 억 개의 행이있는 t0 테이블을 스캔함

스캔하기 위해 프로세스 당 2000만 행을 GpuJoin 및 GpuPreAgg 처리하고 그 결과를 Gather 노드에 결합함

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
psql>
EXPLAIN SELECT cat,count(*),avg(ax)
            FROM t0 NATURAL JOIN t1
           GROUP by cat;
                                   QUERY PLAN
--------------------------------------------------------------------------------
 GroupAggregate  (cost=955705.47..955720.93 rows=27 width=20)
   Group Key: t0.cat
   ->  Sort  (cost=955705.47..955707.36 rows=756 width=44)
         Sort Key: t0.cat
         ->  Gather  (cost=955589.95..955669.33 rows=756 width=44)
               Workers Planned: 4
               ->  Parallel Custom Scan (GpuPreAgg)  (cost=954589.95..954593.73 rows=189 width=44)
                     Reduction: Local
                     GPU Projection: cat, pgstrom.nrows(), pgstrom.nrows((ax IS NOT NULL)), pgstrom.psum(ax)
                     Combined GpuJoin: enabled
                     ->  Parallel Custom Scan (GpuJoin) on t0  (cost=27682.82..841218.52 rows=99996736 width=12)
                           GPU Projection: t0.cat, t1.ax
                           Outer Scan: t0  (cost=0.00..1083384.84 rows=24999184 width=8)
                           Depth 1: GpuHashJoin  (nrows 24999184...99996736)
                                    HashKeys: t0.aid
                                    JoinQuals: (t0.aid = t1.aid)
                                    KDS-Hash (size: 10.39MB)
                           ->  Seq Scan on t1  (cost=0.00..1972.85 rows=103785 width=12)
(18 rows)
 



풀업 기본 계획

PG-Strom은 GPU에서 SCAN, JOIN 및 GROUP BY 워크로드를 실행할 수 있지만 

이러한 사용자 정의 실행 계획이 PostgreSQL의 표준 작업을 단순히 대체하는 경우 최상의 성능으로 작동하지 않음

문제가있는 시나리오의 예는 SCAN이 결과 데이터 세트를 호스트 버퍼에 다시 쓴 후 

동일한 데이터를 GPU로 다시 보내 JOIN을 실행하는 것임

다시 한 번, JOIN 결과가 다시 기록되고 GPU로 전송되어 GROUP BY를 실행함 

이렇게 CPU와 GPU간에 데이터 핑퐁이 발생함


이러한 비효율적 인 작업을 피하기 위해 PG-Strom에는 

단일 GPU 커널 호출에서 많은 작업을 실행하기 위해 하위 계획을 끌어내는 특수 모드가 있음 

작업 블로우를 조합하면 하위 계획이 풀업 될수 있음


SCAN + JOIN

SCAN + GROUP BY

SCAN + JOIN + GROUP BY

http://heterodb.github.io/pg-strom/operations/


아래의 실행 계획 예제는 하위 계획을 끌어 올리지 않음

GpuJoin은 GpuScan의 결과를 수신 한 후 결과를 GpuPreAgg로 전달하여 최종 결과를 생성함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
psql>
EXPLAIN SELECT cat,count(*),avg(ax)
            FROM t0 NATURAL JOIN t1
           WHERE aid < bid
           GROUP BY cat;
                              QUERY PLAN
 
--------------------------------------------------------------------------------
 GroupAggregate  (cost=1239991.03..1239995.15 rows=27 width=20)
   Group Key: t0.cat
   ->  Sort  (cost=1239991.03..1239991.50 rows=189 width=44)
         Sort Key: t0.cat
         ->  Custom Scan (GpuPreAgg)  (cost=1239980.10..1239983.88 rows=189 width=44)
               Reduction: Local
               GPU Projection: cat, pgstrom.nrows(), pgstrom.nrows((ax IS NOT NULL)), pgstrom.psum(ax)
               ->  Custom Scan (GpuJoin)  (cost=50776.43..1199522.96 rows=33332245 width=12)
                     GPU Projection: t0.cat, t1.ax
                     Depth 1: GpuHashJoin  (nrows 33332245...33332245)
                              HashKeys: t0.aid
                              JoinQuals: (t0.aid = t1.aid)
                              KDS-Hash (size: 10.39MB)
                     ->  Custom Scan (GpuScan) on t0  (cost=12634.49..1187710.85 rows=33332245 width=8)
                           GPU Projection: cat, aid
                           GPU Filter: (aid < bid)
                     ->  Seq Scan on t1  (cost=0.00..1972.85 rows=103785 width=12)
(18 rows)

이 예제에서는 각 실행 단계마다 GPU와 호스트 버퍼간에 데이터 핑퐁이 발생하므로 효율성이 떨어지고 성능이 떨어짐

한편, 아래의 쿼리 실행 계획은 하위 계획을 불러옴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
psql>
EXPLAIN ANALYZE SELECT cat,count(*),avg(ax)
                    FROM t0 NATURAL JOIN t1
                   WHERE aid < bid
                   GROUP BY cat;
                              QUERY PLAN
--------------------------------------------------------------------------------
 GroupAggregate  (cost=903669.50..903673.62 rows=27 width=20)
                 (actual time=7761.630..7761.644 rows=27 loops=1)
   Group Key: t0.cat
   ->  Sort  (cost=903669.50..903669.97 rows=189 width=44)
             (actual time=7761.621..7761.626 rows=27 loops=1)
         Sort Key: t0.cat
         Sort Method: quicksort  Memory: 28kB
         ->  Custom Scan (GpuPreAgg)  (cost=903658.57..903662.35 rows=189 width=44)
                                      (actual time=7761.531..7761.540 rows=27 loops=1)
               Reduction: Local
               GPU Projection: cat, pgstrom.nrows(), pgstrom.nrows((ax IS NOT NULL)), pgstrom.psum(ax)
               Combined GpuJoin: enabled
               ->  Custom Scan (GpuJoin) on t0  (cost=12483.41..863201.43 rows=33332245 width=12)
                                                (never executed)
                     GPU Projection: t0.cat, t1.ax
                     Outer Scan: t0  (cost=12634.49..1187710.85 rows=33332245 width=8)
                                     (actual time=59.623..5557.052 rows=100000000 loops=1)
                     Outer Scan Filter: (aid < bid)
                     Rows Removed by Outer Scan Filter: 50002874
                     Depth 1: GpuHashJoin  (plan nrows: 33332245...33332245, actual nrows: 49997126...49997126)
                              HashKeys: t0.aid
                              JoinQuals: (t0.aid = t1.aid)
                              KDS-Hash (size plan: 10.39MB, exec: 64.00MB)
                     ->  Seq Scan on t1  (cost=0.00..1972.85 rows=103785 width=12)
                                         (actual time=0.013..15.303 rows=100000 loops=1)
 Planning time: 0.506 ms
 Execution time: 8495.391 ms
(21 rows)
테이블의 SCAN t0이 GpuJoin에 내장되어 있고 GpuScan이 사라지는 것을 알 수 있음
이것은 GpuJoin가 기본 GpuScan을 불러 온 다음 결합된 GPU 커널 함수에서 WHERE 절의 처리도 한 것을 의미함

이외에 이상하게도, EXPLAIN ANALYZE의 결과는 GpuJoin이 (never executed)로 표시되어 있음
이것은 쿼리 실행 중에 GpuJoin이 절대로 실행되지 않는다는 것을 의미함
GpuPreAgg가 기본 GpuJoin을 가져오고 결합 된 GPU 커널 함수가 JOIN 및 GROUP BY를 수행 하는것을 의미함

SCAN 풀업은 pg_strom.pullup_outer_scan 매개 변수로 제어 할 수 있음
또한 JOIN 풀업은 pg_strom.pullup_outer_join 매개 변수에 의해 제어 할 수 있음
두 매개 변수는 기본적으로 on으로 설정되어 있으며, 일반적으로 이것을 비활성화 할 필요가 없지만, 
문제 발생시 문제를 식별하기 위한 수단으로서 이용 할 수 있음



#참고#
조건절 Pushing 종류
1. 조건절(Predicate) Pushdown : 쿼리 블록 밖에 있는 조건들을 쿼리 블록 안쪽으로 밀어 넣는 것을 말함.
2. 조건절(Predicate) Pullup : 쿼리 블록 안에 있는 조건들을 쿼리 블록 밖으로 내오는 것을 말하며, 그것을 다시 다른 쿼리 블록에 Pushdown하는데 사용함.
3. 조인 조건(Join Predicate) Pushdown : NL 조인 수행 중에 드라이빙 테이블에서 읽은 값은 조건인 Inner 쪽(=right side) 뷰 쿼리 블록 안으로 밀어 넣는 것을 말함.



참조 : http://heterodb.github.io/pg-strom/operations/

https://positivemh.tistory.com/469

http://bysql.net/w201101B/13925