Two more papers, both execution-layer where #403 was storage-layer.
- Abadi, Myers, DeWitt, Madden. Materialization Strategies in a Column-Oriented DBMS.
ICDE 2007.
- Boncz, Zukowski, Nes. MonetDB/X100: Hyper-Pipelining Query Execution. CIDR 2005.
Same rules as #403: worked from the papers, anything we build is ours. Ranked, with what
we already have beside it.
First, the thing we already got right
X100's central argument is that neither extreme works. Tuple-at-a-time Volcano iteration
spends its time in interpretation, and MonetDB/MIL's full column materialization becomes
memory-bandwidth bound. Their answer is vectors sized to stay in CPU cache.
Their measured motivation, TPC-H Q1:
| system |
time |
| MySQL 4.1 |
26.6 s |
| MonetDB/MIL |
3.7 s |
| MonetDB/X100 |
0.50 s |
| hand-coded C |
0.22 s |
MySQL achieved an IPC of 0.7 and spent under 10 percent of its time on the actual
arithmetic.
We already do this. src/columnar.h:47:
#define COLUMNAR_NATIVE_VECTOR_LENGTH 1024 /* values per vector (fixed) */
Storage chunk group defaults to 10,000 rows, and the execution and skip vector is 1024
values. That is the separation X100 argues for, and 1024 is the size range they land on.
Recording it because it is worth knowing we are aligned, and because the next person to
read this paper should not file it as a gap.
1. Position-list intersection before materialization
The item worth measuring. Abadi's late materialization works like this:
first scan R.a and output the positions in R.a that satisfy the predicate. Repeat with
R.b and R.c. Next, use position-wise AND operations to intersect the position lists.
Finally, re-access R.a, R.b, and R.c and extract the values of the records that
satisfy all predicates.
Position lists are cheap to intersect, 32 or 64 at a time when held as bit-strings.
We have the ingredients: vector-level skipping (nativeSkipVec, the
Columnar Vectors Skipped counter), column projection, and deferred slot decode. What I
cannot tell from reading is whether multiple predicates on different columns intersect
their surviving positions before any values are materialized, or whether we materialize the
projected columns for surviving vectors and let a residual qual filter rows afterwards.
The q2 plan from today's work is suggestive but not conclusive:
Columnar Chunk Groups Read: 118 of 667
Columnar Vectors Skipped: 40
Rows Removed by Filter: 17,295,680
17 million rows removed by a filter means 17 million rows reached a filter. Whether they
were materialized first is the question. That is a measurement, not a claim, and it is
the first thing I would establish.
Abadi is also explicit about when late materialization loses:
the column reaccess cost at tuple reconstruction time is high
and worse when positions are not sorted, which is what a join does to them. So this is a
trade with a real downside, not a free win. The paper gives an analytical cost model for
choosing, which is more useful to us than the strategy itself.
2. Branch predication in selection
X100 Figure 2 measures the difference directly:
/* branch version */
if (src[i] < V) out[j++] = i;
/* predicated version */
bool b = (src[i] < V);
out[j] = i;
j += b;
The branch version has a worst case around 50 percent selectivity, where the predictor
cannot win. The predicated version is flat across selectivity and slightly more expensive
on average.
Our filter evaluation runs over 1024-value vectors, which is exactly where this applies.
Whether it is worth doing is a microbenchmark, and the paper says why the answer depends on
selectivity rather than being uniformly yes.
3. Vector size against column count
An open question rather than a finding. 1024 values of an 8 byte type is 8 KB per column.
A vectorized aggregate touching ten columns is working over 80 KB of live vector, which is
past L1 on most parts and into L2.
X100's argument is about staying in cache, so the right vector length may depend on how many
columns an operator touches at once. Ours is a fixed format constant, so this is not a knob
to turn casually. Worth knowing before anyone proposes widening a vectorized operator, and
it connects to the lesson in my own #289 work that eager per-group work regressed narrow
scans.
What I would break out
Only item 1, and only as a measurement first: establish whether multiple predicates on
different columns intersect positions before materializing. If they already do, this issue
closes with a note. If they do not, Abadi's cost model is the right basis for deciding
whether to, because the paper is equally clear about when the strategy loses.
Items 2 and 3 I would leave recorded here. Both are microbenchmark questions, and neither
should be touched without one.
Two more papers, both execution-layer where #403 was storage-layer.
ICDE 2007.
Same rules as #403: worked from the papers, anything we build is ours. Ranked, with what
we already have beside it.
First, the thing we already got right
X100's central argument is that neither extreme works. Tuple-at-a-time Volcano iteration
spends its time in interpretation, and MonetDB/MIL's full column materialization becomes
memory-bandwidth bound. Their answer is vectors sized to stay in CPU cache.
Their measured motivation, TPC-H Q1:
MySQL achieved an IPC of 0.7 and spent under 10 percent of its time on the actual
arithmetic.
We already do this.
src/columnar.h:47:Storage chunk group defaults to 10,000 rows, and the execution and skip vector is 1024
values. That is the separation X100 argues for, and 1024 is the size range they land on.
Recording it because it is worth knowing we are aligned, and because the next person to
read this paper should not file it as a gap.
1. Position-list intersection before materialization
The item worth measuring. Abadi's late materialization works like this:
Position lists are cheap to intersect, 32 or 64 at a time when held as bit-strings.
We have the ingredients: vector-level skipping (
nativeSkipVec, theColumnar Vectors Skippedcounter), column projection, and deferred slot decode. What Icannot tell from reading is whether multiple predicates on different columns intersect
their surviving positions before any values are materialized, or whether we materialize the
projected columns for surviving vectors and let a residual qual filter rows afterwards.
The q2 plan from today's work is suggestive but not conclusive:
17 million rows removed by a filter means 17 million rows reached a filter. Whether they
were materialized first is the question. That is a measurement, not a claim, and it is
the first thing I would establish.
Abadi is also explicit about when late materialization loses:
and worse when positions are not sorted, which is what a join does to them. So this is a
trade with a real downside, not a free win. The paper gives an analytical cost model for
choosing, which is more useful to us than the strategy itself.
2. Branch predication in selection
X100 Figure 2 measures the difference directly:
The branch version has a worst case around 50 percent selectivity, where the predictor
cannot win. The predicated version is flat across selectivity and slightly more expensive
on average.
Our filter evaluation runs over 1024-value vectors, which is exactly where this applies.
Whether it is worth doing is a microbenchmark, and the paper says why the answer depends on
selectivity rather than being uniformly yes.
3. Vector size against column count
An open question rather than a finding. 1024 values of an 8 byte type is 8 KB per column.
A vectorized aggregate touching ten columns is working over 80 KB of live vector, which is
past L1 on most parts and into L2.
X100's argument is about staying in cache, so the right vector length may depend on how many
columns an operator touches at once. Ours is a fixed format constant, so this is not a knob
to turn casually. Worth knowing before anyone proposes widening a vectorized operator, and
it connects to the lesson in my own #289 work that eager per-group work regressed narrow
scans.
What I would break out
Only item 1, and only as a measurement first: establish whether multiple predicates on
different columns intersect positions before materializing. If they already do, this issue
closes with a note. If they do not, Abadi's cost model is the right basis for deciding
whether to, because the paper is equally clear about when the strategy loses.
Items 2 and 3 I would leave recorded here. Both are microbenchmark questions, and neither
should be touched without one.