Skip to content

[ML] Avoid copies and redundant event loops in the RDataLoader - #23761

Merged
siliataider merged 8 commits into
root-project:masterfrom
siliataider:rdataloader-opt-batching
Oct 20, 2026
Merged

siliataider merged 8 commits into
root-project:masterfrom
siliataider:rdataloader-opt-batching

Conversation

@siliataider

@siliataider siliataider commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

This Pull request:

Follow-up of #23642, with one commit per optimization of the RDataLoader memory usage and runtime, in the eager and lazy paths, with and without filters.

Benchmarking results

7256529 rows x 11 cols, batch 262144, 10 epochs, baseline RSS 0.26 GiB, run on a local machine

  • uproot-based loader, 1 thread
  • RDataLoader eager, 1 thread
uproot (in memory) RDataLoader eager before #23473 RDataLoader eager after #23473 RDataLoader eager after #23642 (master) RDataLoader eager in this PR
setup 2.26 0.54 0.43 0.86 0.89
ep1 0.06 9.02 7.49 4.08 3.70
ep2 0.05 0.22 0.15 0.14 0.03
ep3 0.05 0.22 0.04 0.04 0.03
ep4 0.05 0.22 0.04 0.04 0.03
ep5 0.05 0.22 0.04 0.04 0.03
ep6 0.05 0.22 0.04 0.04 0.04
ep7 0.05 0.22 0.04 0.04 0.03
ep8 0.05 0.22 0.04 0.04 0.03
ep9 0.05 0.23 0.04 0.04 0.03
ep10 0.05 0.22 0.04 0.04 0.03
total 2.77 11.56 8.38 5.41 4.90
peak RSS 1.06G 2.69G 1.78G 1.65G 1.40G

@siliataider siliataider self-assigned this Oct 19, 2026
@siliataider siliataider added the in:ML Everything under ROOT/ML label Oct 19, 2026
@siliataider
siliataider force-pushed the rdataloader-opt-batching branch from af5c1c2 to 1a51c27 Compare October 19, 2026 15:45
@siliataider siliataider changed the title [ML] RDataLoader Optimizations [ML] Avoid copies and redundant event loops in the RDataLoader Oct 19, 2026
@siliataider
siliataider marked this pull request as ready for review October 19, 2026 15:52

@vepadulano vepadulano left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks! Some comments from my side.

Comment thread tree/ml/src/RBatchLoader.cxx Outdated
Comment thread tree/ml/src/RDataLoaderEngine.cxx
Comment thread tree/ml/src/RDatasetLoader.cxx Outdated
Comment thread tree/ml/src/RDatasetLoader.cxx
Comment thread tree/ml/inc/ROOT/ML/RDataLoaderEngine.hxx
Comment thread tree/ml/inc/ROOT/ML/RDataLoaderEngine.hxx
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Test Results

    23 files      23 suites   3d 23h 24m 38s ⏱️
 3 891 tests  3 888 ✅ 0 💤 3 ❌
79 795 runs  79 791 ✅ 0 💤 4 ❌

For more details on these failures, see this check.

Results for commit f137c16.

♻️ This comment has been updated with latest results.

The batches read the rows of a chunk or dataset in shuffled order, instead of copying it into a shuffled tensor first. The leftover rows are appended to the leftover batch directly, which is moved to the queue once full.

Assisted-by: Claude Opus 5.5
…sets

The training and validation datasets, and the sampled ones, are filled by reading the loaded rows in shuffled order, instead of building shuffled, sliced and concatenated copies first. The datasets of several dataframes are split directly into the concatenated ones.

Assisted-by: Claude Opus 5.5
The filtered eager path ran one event loop per passing entry, after a `Take` of `rdfentry_` to find them. A single event loop over the filtered dataframe loads the same entries, and the buffer is reserved from the cluster metadata like in the unfiltered case.

Assisted-by: Claude Opus 5.5
The first epoch read each filtered training cluster twice: once to collect `rdfentry_` and find the training/validation boundary, then again to load the training rows. The entries are now taken in the same event loop that loads the cluster, and the validation rows are dropped from the buffer.

Assisted-by: Claude Opus 5.5
`SampleWithReplacement` created and seeded a new generator for every sample, which dominated the oversampling epochs and, with a fixed seed, drew the same sample every time.

Assisted-by: Claude Opus 5.5
The eager epochs copied the whole dataset into batches before returning the first one, so the dataset and all its batches were in memory together. Each batch is now gathered from the dataset when it is requested.

Assisted-by: Claude Opus 5.5
@siliataider
siliataider force-pushed the rdataloader-opt-batching branch from 1a51c27 to 5956f72 Compare October 19, 2026 19:56

@vepadulano vepadulano left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you! LGTM, see minor suggestion.

Comment thread tree/ml/src/RDatasetLoader.cxx Outdated
The dataframes are loaded into a single dataset, and the training and validation sets, as well as the classes of the samplers, are lists of its rows. The split no longer copies the dataset, nor do the samplers, and the batches gather the rows of their set.

Assisted-by: Claude Opus 5.5
… every entry

`RActionCustomLoader::CallExec` allocated a new vector of value addresses for every entry.

Assisted-by: Claude Opus 5.5
@siliataider
siliataider force-pushed the rdataloader-opt-batching branch from 5956f72 to f137c16 Compare October 20, 2026 09:22
@siliataider
siliataider merged commit e18d7f9 into root-project:master Oct 20, 2026
30 of 36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-Assisted in:ML Everything under ROOT/ML

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants