Skip to content

Reduce memcpys in substruct search and improve benchmark autotuning - #240

Merged
scal444 merged 4 commits into
NVIDIA-BioNeMo:mainfrom
scal444:substruct-autotune-and-pattern-upload
Jul 31, 2026
Merged

Reduce memcpys in substruct search and improve benchmark autotuning#240
scal444 merged 4 commits into
NVIDIA-BioNeMo:mainfrom
scal444:substruct-autotune-and-pattern-upload

Conversation

@scal444

@scal444 scal444 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator
  • We had been uploading query patterns for each minibatch, it's now done once up front
  • Default to sanitize, no-sanitize is for preprocessed datasets and should be opt in
  • Increased batch scan and made it log scale
  • Increased default autotune cap, 2k was setup-bound and didnt give useful enough signal

scal444 added 3 commits July 29, 2026 16:27
The batchSize search range was (128, 1024, step=128), and tuning runs were
pinning to 1024 -- the ceiling of the range, not an interior optimum. Raise
the ceiling to 8192, matching what benchmarks/substruct_config.csv already
uses, and switch to log spacing since the useful range spans two orders of
magnitude. The step=128 bought nothing: the runtime derives
targetsPerBatch = batchSize // numQueries, and that truncation destroys any
128 alignment before it reaches a kernel.

Default calibration_max_size to None (uncapped) for tune_substructure. The
2000-target cap left too few mini-batches to fill every worker's executor
ring, so pipeline fill and drain dominated the measurement and large
batchSize values were systematically under-rewarded. auto_subsample and
normalize_calibration_set now accept max_size=None; other tuners keep the
2000 default.

Also default substruct_bench.py to --sanitize. It was the only benchmark
defaulting to sanitize=False, while etkdg_bench and ff_optimize_bench both
default to True. Move the flag pair into a shared bench_utils.cli helper so
the mutually-exclusive group and the safe default stay consistent.
preprocessMiniBatch re-staged and re-uploaded the recursive SMARTS pattern
entries on every pattern sub-batch, of every mini-batch, on every worker.
The data is identical every time: MiniBatchPlanner::prepareRecursiveMiniBatch
points plan.patternsAtDepth at leafSubpatterns_.allQueriesPatternsAtDepth, a
table built once by buildAllPatterns and shared across all mini-batches.

On a 200k-target x 158-query hasSubstructMatch run that was 133,333 uploads
costing 4,979 ms of aggregate worker time -- 44% of all time the six worker
threads spent launching work, and the single largest item in the profile.
Most of it was not the 428-byte copy itself but the handshake around it: the
pinned staging area is only double-buffered, so with four sub-batches per
mini-batch acquireBufferIndex kept returning a buffer whose copy was still
queued behind a full depth level of paint kernels, and waitForBuffer blocked
the worker on cudaEventSynchronize. Those accounted for roughly 135k of the
168k blocking event syncs on worker threads; the actual result drain was only
221 ms.

Upload the table once in LeafSubpatterns::syncToDevice, which already runs on
the setup stream that callers synchronize before starting any worker. Each
sub-batch is a contiguous slice of its depth's entries, so the kernels now
take a pointer into the device table rather than a per-sub-batch staging
buffer, and the whole acquire/wait/fill/copy/record sequence is gone.

The non-pipeline preprocessRecursiveSmarts path builds its own local pattern
vectors and is left unchanged.
@scal444
scal444 requested a review from evasnow1992 July 30, 2026 15:12
Comment thread src/substruct/recursive_preprocessor.cu
Comment thread nvmolkit/autotune/tune_substructure.py
@greptile-apps

greptile-apps Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR reduces repeated GPU pattern transfers and broadens autotuning coverage.

  • Uploads recursive substructure pattern tables once and synchronizes secondary-device publication before worker startup.
  • Changes substructure batch-size tuning to a wider log-uniform range and increases the calibration cap.
  • Makes benchmark sanitization the default and updates autotuning tests for the new search-space shape.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/substruct/recursive_preprocessor.cu Moves immutable recursive pattern tables to device-resident storage shared across minibatches.
src/substruct/substruct_search.cu Synchronizes secondary-device default-stream uploads before workers using independent streams are launched.
nvmolkit/autotune/tune_substructure.py Expands batch-size and calibration sampling while using the existing supported log-uniform tuple format.
nvmolkit/tests/test_autotune.py Updates the default search-space assertion to validate the log-uniform batch-size specification.
benchmarks/substruct_bench.py Makes sanitization the benchmark default while retaining an explicit opt-out.

Reviews (2): Last reviewed commit: "Fix substructure setup ordering and auto..." | Re-trigger Greptile

@evasnow1992 evasnow1992 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank for the fix and optimization. Changes look good to me.

@scal444
scal444 merged commit 4f977de into NVIDIA-BioNeMo:main Jul 31, 2026
10 checks passed
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.

2 participants