From the five-arm ClickBench baseline. It contradicts the reasoning #300 was closed on, so
it seemed worth its own issue rather than a comment on a closed one.
The measurement
11,110,833 rows, 105 columns, one cluster, one run, the same TSV file loaded by every
PostgreSQL arm through COPY.
| arm |
load |
cost over heap |
size on disk |
| heap |
140.8 s |
1.00x |
7.82 GB |
| Citus columnar |
184.1 s |
1.31x |
1.66 GB |
| pgColumnar |
548.7 s |
3.90x |
1.48 GB |
| DuckDB |
28.2 s |
0.20x |
3.22 GB |
Citus columnar pays 31 percent over heap to write columnar storage. We pay 290 percent.
We are 3.0x slower than Citus on the same file, in the same cluster, through the same core
COPY parser.
Why this contradicts #300's closing reasoning
#300 concluded that the residual gap to heap is dominated by PostgreSQL's own input path:
A profile of the post-#155 load shows that gap is dominated by PostgreSQL's own COPY
input path, not by anything pgColumnar-specific [...] Heap and TimescaleDB pay this
identically, because every engine funnels a text COPY through the same core parser and
per-row executor pipeline.
That reasoning is sound and the profile was real. But it predicts every columnar TAM lands
near the same number, because they all pay the same parse. Citus is a columnar table
access method going through the identical core parser and it lands at 1.31x.
So the parse floor is not what separates us from heap. Whatever is above it, in our own
write path, is now the dominant term. #300 could not see this because it compared against
heap and TimescaleDB. TimescaleDB compresses in a background job after the insert, so its
load number is not the same operation. Citus is the like-for-like comparison and nobody
had run it.
The Citus arm was only possible from today: before #429 a server with both extensions
preloaded refused to start (#428).
The honest caveat
We compress better: 1.48 GB against 1.66 GB, 12 percent smaller. Some of the extra time
buys that. But 12 percent smaller does not explain 3 times slower, and the trade as it
stands is a poor one to offer a user.
I have not profiled where our time goes, and I am not going to guess. #300's own method,
a profile of the load, is the right next step and it now has a specific question to answer:
what do we do per row that Citus does not.
Why it matters beyond the number
Load time is one of two figures ClickBench grades, alongside on-disk size, and it is the
first thing a user measures when trying a storage extension. A 3x deficit against the
nearest comparable extension is a competitive fact, not a micro-optimisation.
Reproducing
bench/run_clickbench.sh with PGC_CB_ARMS=heap,columnar,citus, on the branch in #424.
The Citus arm asserts its access method registered rather than assuming it, so a silent
fallback to heap cannot be mistaken for a fast columnar load.
From the five-arm ClickBench baseline. It contradicts the reasoning #300 was closed on, so
it seemed worth its own issue rather than a comment on a closed one.
The measurement
11,110,833 rows, 105 columns, one cluster, one run, the same TSV file loaded by every
PostgreSQL arm through
COPY.Citus columnar pays 31 percent over heap to write columnar storage. We pay 290 percent.
We are 3.0x slower than Citus on the same file, in the same cluster, through the same core
COPYparser.Why this contradicts #300's closing reasoning
#300 concluded that the residual gap to heap is dominated by PostgreSQL's own input path:
That reasoning is sound and the profile was real. But it predicts every columnar TAM lands
near the same number, because they all pay the same parse. Citus is a columnar table
access method going through the identical core parser and it lands at 1.31x.
So the parse floor is not what separates us from heap. Whatever is above it, in our own
write path, is now the dominant term. #300 could not see this because it compared against
heap and TimescaleDB. TimescaleDB compresses in a background job after the insert, so its
load number is not the same operation. Citus is the like-for-like comparison and nobody
had run it.
The Citus arm was only possible from today: before #429 a server with both extensions
preloaded refused to start (#428).
The honest caveat
We compress better: 1.48 GB against 1.66 GB, 12 percent smaller. Some of the extra time
buys that. But 12 percent smaller does not explain 3 times slower, and the trade as it
stands is a poor one to offer a user.
I have not profiled where our time goes, and I am not going to guess. #300's own method,
a profile of the load, is the right next step and it now has a specific question to answer:
what do we do per row that Citus does not.
Why it matters beyond the number
Load time is one of two figures ClickBench grades, alongside on-disk size, and it is the
first thing a user measures when trying a storage extension. A 3x deficit against the
nearest comparable extension is a competitive fact, not a micro-optimisation.
Reproducing
bench/run_clickbench.shwithPGC_CB_ARMS=heap,columnar,citus, on the branch in #424.The Citus arm asserts its access method registered rather than assuming it, so a silent
fallback to heap cannot be mistaken for a fast columnar load.