Skip to content

Perf: 2-3x faster solver, identical output - #17

Open
erelShtosselCEVA wants to merge 1 commit into
google:mainfrom
erelShtosselCEVA:perf/solver-hot-path
Open

erelShtosselCEVA wants to merge 1 commit into
google:mainfrom
erelShtosselCEVA:perf/solver-hot-path

Conversation

@erelShtosselCEVA

@erelShtosselCEVA erelShtosselCEVA commented Sep 6, 2026

Copy link
Copy Markdown

Hi @mmoffitt!

Thank you for the amazing work!

I've used Claude to optimize the code, resulting in about x2-3 faster
runtime without changing the results. I checked it on the benchmarks
published in this repo, and an internal benchmark of 40K examples.


Three changes to the solver's hot path. No algorithmic or behavioural
change: output is byte-identical on all 11 benchmarks/challenging
inputs, --validate passes on all of them, and the test suite is green.

1. Section and BufferData::overlaps become sorted vectors

Section was absl::flat_hash_set<BufferIdx> and overlaps was
absl::btree_set<Overlap>. Both are written once during Sweep and only
ever iterated afterwards, never queried. UpdateSectionData walks
sweep_result_.sections[s_idx] once per affected section per node, so
hash-set iteration (which skips empty slots) shows up directly. Sweep
still uses a hash set for the mutable active/alive sets and converts on
the way out.

Each ordered buffer pair is visited exactly once, so overlaps needs no
dedup, only a sort at the end of Sweep to preserve the order the
btree_set previously provided.

2. Persistent undo trails instead of per-candidate vectors

UpdateSectionData and UpdateMinOffsets each returned a freshly
allocated vector, affected_sections was a flat_hash_set constructed
inside the offset loop, and ComputeOrdering returned a vector by value.
All four were allocated for every candidate offset, including the large
majority rejected by Check.

They are now member trails: callers record size() on entry and unwind to
that mark on exit. affected_sections uses a generation-stamped vector
(O(1) dedup, no hashing, no allocation). ordering needs one reusable
vector per recursion depth because it is handed to the recursive call as
orig_ordering; this uses a std::deque so references stay valid as the
pool grows.

Measured with an LD_PRELOAD malloc interposer on
benchmarks/challenging/A.1048576.csv: 15,662,633 -> 119,995 mallocs.

3. Throttled deadline check

SearchSolutions called absl::Now() on every node even when
params_.timeout is InfiniteDuration (the default). Now the clock is
only read when a finite timeout was requested, and then once per 1024
nodes. cancelled_ is loaded relaxed.

Also adds a fast path to Buffer::effective_size and CreatePoints for
buffers with no gaps, which avoids an allocation and a sort per
overlapping pair. This is a no-op on the shipped benchmarks (only 150-215
buffers each) but matters at larger buffer counts.

Results

Built with -O2 -DNDEBUG on a single core, using the solver's own
reported elapsed time, summed over all 11 benchmarks/challenging inputs
at --capacity=1048576:

total
before 39.0s
after 13.0s

Test change

tests/sweeper_test.cc had three .sections literals written in
non-ascending order ({1, 0}, {1, 3, 2}, {3, 2}), which only compared
equal under unordered-set semantics. They are sorted to match the new
vector type.


P.S. the Python integration works locally, can you push it to PyPI with
Windows and Linux wheels?

Removes per-candidate heap allocations from the DFS and replaces two
read-hot associative containers with sorted vectors. Solver output is
unchanged. Optimization done with Claude.
@google-cla

google-cla Bot commented Sep 6, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant