Skip to content

Comments

execute_plan: don't build temporary vec of rows#2918

Merged
Centril merged 5 commits intomasterfrom
centril/execute-plan-no-temp-vecs
Jul 30, 2025
Merged

execute_plan: don't build temporary vec of rows#2918
Centril merged 5 commits intomasterfrom
centril/execute-plan-no-temp-vecs

Conversation

@Centril
Copy link
Contributor

@Centril Centril commented Jul 4, 2025

Description of Changes

Avoid building a temporary Vec in execute_plan by exposing a list-building interface instead.

  • The old fn encode_list is rewritten in terms of this list-building interface.
  • The BsatnRowList and BsatnRowListBuilder types are split into two entirely separate types. The latter now tries to recognize the case where there isn't a known static layout, but where the BSATN lengths happen to be the same for all rows anyways. In those cases, the allocation of RowSizeHint::RowOffsets is avoided in favor of just storing the found length in bytes. This is in particular useful for small table updates as statistically, the fewer rows, the more chance of the lengths being all equal. In the case of a single row, the chance is notably 100%. It is also good for the case of when we don't have RelValue::Row or Row::Ptr but where the underlying table that actually has a static layout.

In the future, we might want to avoid these lists in incremental as well.

Benchmarks

Benchmark numbers vs. master using cargo bench --bench subscription -- --baseline subs on i7-7700K, 64GB RAM:

footprint-scan          time:   [28.731 ms 28.924 ms 29.171 ms]
                        change: [-49.728% -49.006% -48.388%] (p = 0.00 < 0.05)
                        Performance has improved.

Performance goes from roughly 56.721 ms to 28.795 ms.

API and ABI breaking changes

None

Expected complexity level and risk

2, fairly local change to just subscriptions.

Testing

Covered by existing tests.

@Centril Centril requested a review from gefjon as a code owner July 4, 2025 09:00
@Centril Centril requested a review from joshua-spacetime July 4, 2025 09:00
@Centril Centril force-pushed the centril/execute-plan-no-temp-vecs branch from 54e7434 to b45b193 Compare July 4, 2025 14:20
Copy link
Contributor

@gefjon gefjon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good code, good comments, good benchmarks. A lot of this RowListBuilder stuff could be moved out of the code-ownered websocket.rs, IMO, since we're trying to restrict changes to the format of the messages, not the procedures by which they are constructed. But I don't think it needs to get moved, I'm just flagging it as an option.

@joshua-spacetime
Copy link
Collaborator

I would like to review this before it merges.

A lot of this RowListBuilder stuff could be moved out of the code-ownered websocket.rs

In particular I may ask that we do this.

@bfops bfops added release-any To be landed in any release window performance A PR/Issue related to improving performance of stdb labels Jul 7, 2025
@Centril
Copy link
Contributor Author

Centril commented Jul 8, 2025

In particular I may ask that we do this.

I think we should split websocket.rs into several pieces, but as #2911 builds upon this PR and also changes websocket.rs, I don't think we should split in this PR.

Copy link
Collaborator

@joshua-spacetime joshua-spacetime left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think it is an opportune time to move the non-spacetime types out of websocket.rs. This file should really be reserved for the public message schemas only.

@Centril Centril force-pushed the centril/execute-plan-no-temp-vecs branch from 6271604 to c134d82 Compare July 15, 2025 17:08
@Centril Centril requested a review from joshua-spacetime July 15, 2025 17:09
@Centril Centril dismissed joshua-spacetime’s stale review July 30, 2025 21:52

Addressed core of review. Further refactorings aren't related to PR's goals and can be done in follow ups.

@Centril Centril added this pull request to the merge queue Jul 30, 2025
Merged via the queue into master with commit 5e0c0f7 Jul 30, 2025
24 of 25 checks passed
@Centril Centril deleted the centril/execute-plan-no-temp-vecs branch July 30, 2025 22:46
mamcx pushed a commit that referenced this pull request Aug 26, 2025
# Description of Changes

Avoid building a temporary `Vec` in `execute_plan` by exposing a
list-building interface instead.

- The old `fn encode_list` is rewritten in terms of this list-building
interface.
- The `BsatnRowList` and `BsatnRowListBuilder` types are split into two
entirely separate types. The latter now tries to recognize the case
where there isn't a known static layout, but where the BSATN lengths
happen to be the same for all rows anyways. In those cases, the
allocation of `RowSizeHint::RowOffsets` is avoided in favor of just
storing the found length in bytes. This is in particular useful for
small table updates as statistically, the fewer rows, the more chance of
the lengths being all equal. In the case of a single row, the chance is
notably 100%. It is also good for the case of when we don't have
`RelValue::Row` or `Row::Ptr` but where the underlying table that
actually has a static layout.

In the future, we might want to avoid these lists in incremental as
well.

# Benchmarks

Benchmark numbers vs. master using `cargo bench --bench subscription --
--baseline subs` on i7-7700K, 64GB RAM:
```
footprint-scan          time:   [28.731 ms 28.924 ms 29.171 ms]
                        change: [-49.728% -49.006% -48.388%] (p = 0.00 < 0.05)
                        Performance has improved.
```

Performance goes from roughly 56.721 ms to 28.795 ms.

# API and ABI breaking changes

None

# Expected complexity level and risk

2, fairly local change to just subscriptions.

# Testing

Covered by existing tests.
github-merge-queue bot pushed a commit that referenced this pull request Dec 18, 2025
# Description of Changes

Fixes #2824.

Defines a global pool `BsatnRowListBuilderPool` which reclaims the
buffers of a `ServerMessage<BsatnFormat>` and which is then used when
building new `ServerMessage<BsatnFormat>`s.

Notes:
1. The new pool `BsatnRowListBuilderPool` reports the same kind of
metrics to prometheus as `PagePool` does.
2. `BsatnRowListBuilder` now works in terms of `BytesMut`.
3. The trait method `fn to_bsatn_extend` is redefined to be capable of
dealing with `BytesMut` as well as `Vec<u8>`.
4. A trait `ConsumeEachBuffer` is defined from
`ServerMessage<BsatnFormat>` and down to extract buffers.
`<ServerMessage<_> as ConsumeEachBuffer>::consume_each_buffer(...)` is
then called in `messages::serialize(...)` just after bsatn-encoding the
entire message and before any compression is done. This is the place
where the pool reclaims buffers.

# Benchmarks

Benchmark numbers vs. master using `cargo bench --bench subscription --
--baseline subs` on i7-7700K, 64GB RAM:

```
footprint-scan          time:   [21.607 ms 21.873 ms 22.187 ms]
                        change: [-62.090% -61.438% -60.787%] (p = 0.00 < 0.05)
                        Performance has improved.

full-scan               time:   [22.185 ms 22.245 ms 22.324 ms]
                        change: [-36.884% -36.497% -36.166%] (p = 0.00 < 0.05)
                        Performance has improved.
```

The improvements in `footprint-scan` are mostly thanks to
#2918, but 7 ms of the
improvements here are thanks to the pool. The improvements to
`full-scan` should be only thanks to the pool.

# API and ABI breaking changes

None

# Expected complexity level and risk

2?

# Testing

- Tests for `Pool<T>` also apply to `BsatnRowListBuilderPool`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance A PR/Issue related to improving performance of stdb release-any To be landed in any release window

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants