Skip to content

Bulk load is 4.9x slower than heap, and import inherits it #155

Description

@jdatcmd

Plan: design/IMPORT_THROUGHPUT_PLAN.md.

docs/benchmarks.md called import "about 18x slower than export" and blamed the
full insert path including index maintenance. Measuring it says that description
is wrong twice over, and the real target is different.

Measured, PG17.10 non-assert, 6,000,000 rows, 5 columns

step ms
heap INSERT INTO ... SELECT 2,667.9
columnar INSERT INTO ... SELECT, no file involved 12,989.6
export_arrow 881.9
import_arrow 12,150.2
full scan of the Parquet file through read_parquet 1,415.0
import_parquet 12,956.6
columnar INSERT, exhaustive selection (encoding_sample_rows = 0) 20,103.2

Import is not slower than an ordinary insert: 12,150 ms against 12,990 ms for
the same rows with no file involved. There is no import-specific overhead. The
real statement is that the columnar write path is about 15x slower than the read
path, and 4.9x slower than a heap insert of the same rows, which is the wrong
direction for a format that writes about a hundredth of the bytes.

The readers are not the bottleneck: reading the whole Parquet file costs
1,415 ms, 11% of import. The other 89% is the write path.

Encoding selection is not the bulk of it either: turning sampling off costs
55% more, so what remains after sampling is a minority of the write path.

Index maintenance is not in the path at all, which is its own bug: see #153.

Where the waste is

Both readers decode a column-oriented file into per-row Datum arrays, hand each
row to table_tuple_insert, and the write path copies each value back into
per-column buffers. Arrow and PGCN are both columnar; the row exists only for the
length of one call between two column stores.

The plan proposes a batching entry point first, then column-at-a-time transfer for
the cases where the representations already agree, with the reader cost of
1,415 ms as the floor and 3 to 5 seconds as the plausible target against today's
13.

Acceptance

The claim is a ratio against the read_parquet floor rather than a millisecond
count, so it means the same thing on other hardware. Existing round-trip suites
must pass untouched, and should run against an indexed target once #153 is
resolved.

Estimated 2 to 4 dev-months for two people.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions