Skip to content

Commit 781ce79

Browse files
zhangh43xiong-gangNing Yu
committed
Initial Commit.
Co-authored-by: Gang Xiong <[email protected]> Co-authored-by: Ning Yu <[email protected]>
0 parents  commit 781ce79

38 files changed

+9317
-0
lines changed

Makefile

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
MODULE_big = vectorize_engine
2+
3+
EXTENSION = vectorize_engine
4+
DATA = vectorize_engine--1.0.sql
5+
6+
PGFILEDESC = "vectorize engine for PostgreSQL"
7+
8+
REGRESS = vectorize_engine
9+
10+
OBJS += vectorEngine.o nodeSeqscan.o nodeAgg.o nodeUnbatch.o execScan.o plan.o utils.o execTuples.o execQual.o vectorTupleSlot.o
11+
OBJS += vtype/vtype.o vtype/vtimestamp.o vtype/vint.o vtype/vfloat.o vtype/vpseudotypes.o vtype/vvarchar.o vtype/vdate.o
12+
13+
# print vectorize info when compile
14+
# PG_CFLAGS = -fopt-info-vec
15+
16+
PG_CFLAGS = -Wno-int-in-bool-context
17+
PG_CONFIG = pg_config
18+
PGXS := $(shell $(PG_CONFIG) --pgxs)
19+
include $(PGXS)

README.md

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
## About
2+
Vectorize Execution is an extension for Postgres which utilize vectorized technique to accelerate query execution.
3+
4+
Vectorize Execution is based on Postgres 9.6 now, and will support newer Postgres version soon.
5+
6+
## Design
7+
Below are features in our design.
8+
1. Pure extension. We didn't hack any code into postgres kernel.
9+
2. CustomScan node. We use CustomScan framework to replace original executor node such as SeqScan, Agg etc.
10+
Based on CustomScan, we could extend the CustomScanState, BeginCustomScan(), ExecCustomScan(), EndCustomScan()
11+
interface and implement vectorize executor node.
12+
3. Post planner hook. After plan is generated, we use plan_tree_walker to traverse the plan tree and check whether
13+
it could be vectorized. If yes, we will replace non-vectorized node(seqscan, agg etc.) with vectorized node(in
14+
form of customscan node) and use vectorized executor. If no, we will revert to the original plan and use non-vectorized executor.
15+
4. Inherit original executor code. Instead of rewriting the whole executor, we choose a more smooth method to modify
16+
current Postgres executor node and make it vectorized. We copy the current executor node into our extension, and
17+
add vectorize logic based on it. When Postgres enchance its executor, we could relatively easily merge them back.
18+
5. Pluggable storage. Postgres has supported pluggable storage now. TupleTableSlot is refactored as abstract struct
19+
TupleTableSlotOps. We will implement VectorTupleTableSlot in our extension when we upgrade the extension to latest PG.
20+
21+
## Usage
22+
1. Add GCC SIMD support option in configure when building Postgres. `-march=native`
23+
2. Build & Install. `cd vectorize_engine; make install`
24+
3. Config postgres.conf & Restart database. `shared_preload_libraries = 'vectorize_engine'`
25+
4. Run test. `make installcheck`
26+
5. Initialize at database level. `create extension vectorize_engine;`
27+
6. Enable by GUC(default off). `set enable_vectorize_engine to on;`
28+
29+
## Performance
30+
We run TPC-H 10G Q1 on machine at GCP(24G memory, 8 Core Intel(R) Xeon(R) CPU @ 2.20GHz).
31+
32+
standard PG run 50s and PG with vectorize engine version run 28s.
33+
34+
lineitem is stored as heap table with schema is as follows
35+
```
36+
Table "public.lineitem"
37+
Column | Type | Modifiers
38+
-----------------+-----------------------+-----------
39+
l_orderkey | bigint | not null
40+
l_partkey | integer | not null
41+
l_suppkey | integer | not null
42+
l_linenumber | integer | not null
43+
l_quantity | double precision | not null
44+
l_extendedprice | double precision | not null
45+
l_discount | double precision | not null
46+
l_tax | double precision | not null
47+
l_returnflag | character(1) | not null
48+
l_linestatus | character(1) | not null
49+
l_shipdate | date | not null
50+
l_commitdate | date | not null
51+
l_receiptdate | date | not null
52+
l_shipinstruct | character(25) | not null
53+
l_shipmode | character(10) | not null
54+
l_comment | character varying(44) | not null
55+
```
56+
57+
58+
TPC-H Q1 is
59+
```
60+
select
61+
l_returnflag,
62+
l_linestatus,
63+
sum(l_quantity) as sum_qty,
64+
sum(l_extendedprice) as sum_base_price,
65+
sum(l_extendedprice * (1 - l_discount)) as sum_disc_price,
66+
sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge,
67+
avg(l_quantity) as avg_qty,
68+
avg(l_extendedprice) as avg_price,
69+
avg(l_discount) as avg_disc,
70+
count(l_discount) as count_order
71+
from
72+
lineitem1
73+
where
74+
l_shipdate <= date '1998-12-01' - interval '106 day'
75+
group by
76+
l_returnflag,
77+
l_linestatus;
78+
```
79+

execQual.c

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#include "postgres.h"
2+
3+
#include "access/htup_details.h"
4+
#include "access/nbtree.h"
5+
#include "access/tupconvert.h"
6+
#include "catalog/objectaccess.h"
7+
#include "catalog/pg_type.h"
8+
#include "executor/execdebug.h"
9+
#include "executor/nodeSubplan.h"
10+
#include "funcapi.h"
11+
#include "miscadmin.h"
12+
#include "nodes/makefuncs.h"
13+
#include "nodes/nodeFuncs.h"
14+
#include "optimizer/planner.h"
15+
#include "parser/parse_coerce.h"
16+
#include "parser/parsetree.h"
17+
#include "pgstat.h"
18+
#include "utils/acl.h"
19+
#include "utils/builtins.h"
20+
#include "utils/lsyscache.h"
21+
#include "utils/memutils.h"
22+
#include "utils/typcache.h"
23+
#include "utils/xml.h"
24+
25+
#include "executor.h"
26+
#include "execTuples.h"
27+
#include "vectorTupleSlot.h"
28+
29+
/* ----------------------------------------------------------------
30+
* ExecQual
31+
*
32+
* Evaluates a conjunctive boolean expression (qual list) and
33+
* returns true iff none of the subexpressions are false.
34+
* (We also return true if the list is empty.)
35+
*
36+
* If some of the subexpressions yield NULL but none yield FALSE,
37+
* then the result of the conjunction is NULL (ie, unknown)
38+
* according to three-valued boolean logic. In this case,
39+
* we return the value specified by the "resultForNull" parameter.
40+
*
41+
* Callers evaluating WHERE clauses should pass resultForNull=FALSE,
42+
* since SQL specifies that tuples with null WHERE results do not
43+
* get selected. On the other hand, callers evaluating constraint
44+
* conditions should pass resultForNull=TRUE, since SQL also specifies
45+
* that NULL constraint conditions are not failures.
46+
*
47+
* NOTE: it would not be correct to use this routine to evaluate an
48+
* AND subclause of a boolean expression; for that purpose, a NULL
49+
* result must be returned as NULL so that it can be properly treated
50+
* in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
51+
* This routine is only used in contexts where a complete expression
52+
* is being evaluated and we know that NULL can be treated the same
53+
* as one boolean result or the other.
54+
*
55+
* ----------------------------------------------------------------
56+
*/
57+
bool
58+
VExecScanQual(List *qual, ExprContext *econtext, bool resultForNull)
59+
{
60+
MemoryContext oldContext;
61+
TupleTableSlot *slot;
62+
VectorTupleSlot *vslot;
63+
ListCell *l;
64+
int row;
65+
66+
/*
67+
* debugging stuff
68+
*/
69+
EV_printf("ExecQual: qual is ");
70+
EV_nodeDisplay(qual);
71+
EV_printf("\n");
72+
73+
/*
74+
* Run in short-lived per-tuple context while computing expressions.
75+
*/
76+
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
77+
78+
/*
79+
* Evaluate the qual conditions one at a time. If we find a FALSE result,
80+
* we can stop evaluating and return FALSE --- the AND result must be
81+
* FALSE. Also, if we find a NULL result when resultForNull is FALSE, we
82+
* can stop and return FALSE --- the AND result must be FALSE or NULL in
83+
* that case, and the caller doesn't care which.
84+
*
85+
* If we get to the end of the list, we can return TRUE. This will happen
86+
* when the AND result is indeed TRUE, or when the AND result is NULL (one
87+
* or more NULL subresult, with all the rest TRUE) and the caller has
88+
* specified resultForNull = TRUE.
89+
*/
90+
91+
slot = econtext->ecxt_scantuple;
92+
vslot = (VectorTupleSlot *)slot;
93+
foreach(l, qual)
94+
{
95+
ExprState *clause = (ExprState *) lfirst(l);
96+
Datum expr_value;
97+
bool isNull;
98+
vbool *expr_val_bools;
99+
100+
/* take a batch as input to evaluate quals */
101+
expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
102+
103+
expr_val_bools = (vbool *)DatumGetPointer(expr_value);
104+
105+
/* using skip array to indicated row which didn't pass the qual */
106+
for(row = 0; row < BATCHSIZE; row++)
107+
if((!expr_val_bools->isnull[row] || !resultForNull) &&
108+
!DatumGetBool(expr_val_bools->values[row]) &&
109+
!vslot->skip[row])
110+
vslot->skip[row] = true;
111+
/* TODO: opt: add skipped count for vslot to support skipping the whole batch?*/
112+
}
113+
114+
MemoryContextSwitchTo(oldContext);
115+
116+
/* return true if any tuple in batch pass the qual. */
117+
for(row = 0; row < BATCHSIZE; row++)
118+
if (!vslot->skip[row])
119+
return true;
120+
121+
return false;
122+
}
123+

0 commit comments

Comments
 (0)