Skip to content

perf(group): cut the paired assigner's per-UMI encoding, allocation, and iteration - #625

Merged
nh13 merged 1 commit into
mainfrom
nh/paired-assigner-allocs
Jul 24, 2026
Merged

nh13 merged 1 commit into
mainfrom
nh/paired-assigner-allocs

Conversation

@nh13

@nh13 nh13 commented Jul 21, 2026 •

Copy link
Copy Markdown
Member

Summary

Stacked on #623. Together with that PR, this removes the per-UMI overhead the sequential PairedUmiAssigner gained — the source of the group paired-strategy slowdown, which a full staircase bisect across v0.3.1→main attributes entirely to a single commit (the differing-length/invalid-UMI GRP-01 correctness fix), with every other commit in the range flat and the identity control flat throughout.

#623 removed the redundant encodes. This removes the rest of the per-UMI work, all in PairedUmiAssigner:

  • underlying_umi_len — replace the split('-') / rsplit(':') iterators plus a double BitEnc::from_umi_str encode with a single byte scan (position/rposition + an ACGT count). The packed value was always discarded — only validity and base length were ever used. Kept bug-for-bug equivalent to from_umi_str (both cases accepted, >32-bases-per-half rejected); prefix bytes before : are never validated, so a bb: orientation prefix is ignored rather than rejected.
  • Single-molecule fast path — decide the strand with a new allocation-free orientation_is_ab helper instead of building a reversed String per read. umi < reverse(umi) reduces to comparing the two dash-halves, because - (0x2D) is smaller than every byte that can appear in a half.
  • matches_paired — compare reverse(lhs) to rhs via reversed_matches_within (a positional byte compare) instead of allocating the reversed string on every adjacency-graph comparison.
  • upper_umis — borrow raw_umis directly when no UMI contains a lowercase byte (the production case), allocating an uppercased copy only for mixed-case input.
  • Paired-format validation — locate the first - and confirm no second, instead of split('-').count().

Correctness

Behaviour-preserving. Grouping output is unchanged: family-size histograms and molecule counts are byte-identical before and after (agilent-hs2 paired: 10,220,662 molecules; identity: 10,292,162). The GRP-01 cross-assigner parity guarantees are untouched (test_sequential_and_parallel_assigners_induce_same_partition passes). Each rewritten helper is pinned to the definition it replaces by a new rstest table:

  • test_underlying_umi_len_matches_bitenc (10 cases: prefixes, both cases, non-ACGT, the 32-base limit, empty/uneven halves)
  • test_orientation_is_ab_matches_reverse_compare (7 cases vs umi < reverse(umi))
  • test_reversed_matches_within_matches_alloc (8 cases vs reverse-then-compare, incl. threshold boundaries)

Full workspace suite: 5614 passed, 23 skipped. ci-fmt / ci-lint clean.

Performance

Measured on a c7g.4xlarge (Graviton3, matching the benchmark fleet), --threads 0, GNU time -v, agilent-hs2 paired, with identity as a flat control. Against the pre-regression baseline (the offending commit's parent):

paired user CPU vs pre-regression
offending commit 92.96s +4.1%
#623 alone ~92.5s +3.9%
#623 + this PR ~90.1s ~+1%

This recovers roughly three-quarters of the paired regression on this workload; the residual ~1% is the irreducible per-UMI validity/length check the GRP-01 fix requires.

Scope note (measured honestly): agilent-hs2 is high-diversity, so its groups take the single-molecule fast path and the adjacency graph is cold. On that workload the matches_paired and upper_umis changes are neutral — the recovery above comes from the underlying_umi_len and fast-path-strand changes. matches_paired's per-comparison allocation and the upper_umis allocation are nonetheless real; they are expected to help adjacency-heavy / low-diversity inputs (deep panels), which this measurement does not exercise. They are included as correctness-preserving allocation hygiene, not because they move this benchmark.

Summary by CodeRabbit

  • Performance Improvements

    • Improved paired-UMI assignment by reducing temporary allocations and optimizing paired format/validation checks.
    • Faster reverse-orientation comparison with early mismatch detection.
  • Bug Fixes

    • More accurate UMI length handling according to supported encoding rules (including prefixed keys and invalid bases).
    • Corrected strand/orientation determination and paired-format handling, including lowercase and edge cases.
  • Tests

    • Added targeted regression coverage for UMI length semantics, orientation logic, and reverse matching behavior.

@nh13
nh13 temporarily deployed to github-actions July 21, 2026 19:11 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f480c459-8d72-477f-90f4-863445083b53

📥 Commits

Reviewing files that changed from the base of the PR and between e85bca8 and ece62be.

📒 Files selected for processing (1)
  • crates/fgumi-umi/src/assigner.rs

Walkthrough

Paired UMI assignment now performs byte-level BitEnc-compatible validation, avoids unnecessary uppercase and reverse-string allocations, and preserves strand and matching behavior. Tests cover prefixes, invalid bases, length limits, orientations, and mismatch thresholds.

Changes

Paired UMI assignment

Layer / File(s) Summary
Validation and orientation handling
crates/fgumi-umi/src/assigner.rs
Paired parsing enforces one delimiter and per-half encoding limits, uppercasing is conditional, and strand selection uses allocation-free orientation comparison.
Allocation-free reverse matching
crates/fgumi-umi/src/assigner.rs
Reverse matching compares virtual positions with early mismatch termination; tests verify equivalence with the previous implementation and cover length and encoding edge cases.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the paired assigner performance work and its main optimization theme.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch nh/paired-assigner-allocs

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 21, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.50000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.57%. Comparing base (62fd0c3) to head (ece62be).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
crates/fgumi-umi/src/assigner.rs 97.50% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #625      +/-   ##
==========================================
+ Coverage   93.53%   93.57%   +0.03%     
==========================================
  Files         175      175              
  Lines      106571   106634      +63     
==========================================
+ Hits        99685    99780      +95     
+ Misses       6886     6854      -32     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@nh13
nh13 force-pushed the nh/assigner-encode-once branch from df0cd7b to 7dde8dc Compare July 21, 2026 19:16
@nh13
nh13 force-pushed the nh/paired-assigner-allocs branch from b88d273 to 82ee453 Compare July 21, 2026 19:16
@nh13
nh13 temporarily deployed to github-actions July 21, 2026 19:16 — with GitHub Actions Inactive
@nh13
nh13 force-pushed the nh/assigner-encode-once branch from 7dde8dc to 70f2a73 Compare July 23, 2026 05:22
@nh13
nh13 force-pushed the nh/paired-assigner-allocs branch from 82ee453 to 69d78e4 Compare July 23, 2026 07:16
@nh13
nh13 temporarily deployed to github-actions July 23, 2026 07:17 — with GitHub Actions Inactive
Base automatically changed from nh/assigner-encode-once to main July 23, 2026 08:50
@nh13

nh13 commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13
nh13 force-pushed the nh/paired-assigner-allocs branch from 69d78e4 to e85bca8 Compare July 23, 2026 16:12
@nh13
nh13 temporarily deployed to github-actions July 23, 2026 16:12 — with GitHub Actions Inactive
@nh13

nh13 commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/fgumi-umi/src/assigner.rs`:
- Around line 2911-2992: Add a programmatically generated fgbio baseline test
for the changed UMI assignment path, comparing assignment partitions and A/B
strand identities rather than only local helper results. Generate cases covering
orientation ties, empty and singleton families, asymmetric halves, and relevant
length boundaries, and assert the implementation matches the fgbio output for
every generated case.
- Around line 1854-1865: Update orientation_is_ab to preserve the full lexical
ordering of the original paired UMI, including accepted prefixes, by comparing
umi with a virtual reverse-order second-first representation rather than
comparing split halves directly. Ensure assign() selects the same A/B strand as
umi < reverse(umi), and add coverage for the valid prefixed case A-A!: where the
comparison must return false.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 40ec937e-f305-44c2-b118-26ccd5f23d8a

📥 Commits

Reviewing files that changed from the base of the PR and between 72adf52 and e85bca8.

📒 Files selected for processing (1)
  • crates/fgumi-umi/src/assigner.rs

Comment thread crates/fgumi-umi/src/assigner.rs
Comment thread crates/fgumi-umi/src/assigner.rs
@nh13
nh13 force-pushed the nh/paired-assigner-allocs branch from e85bca8 to 34ec4e8 Compare July 23, 2026 18:06
@nh13
nh13 temporarily deployed to github-actions July 23, 2026 18:06 — with GitHub Actions Inactive
…and iteration

Follows the encode-once change with the rest of the sequential paired assigner's
per-UMI overhead, identified by bisect as the source of the group paired-strategy
slowdown and confirmed by profiling on a c7g.4xlarge.

- underlying_umi_len: replace split('-')/rsplit(':') iterators plus a double
  BitEnc::from_umi_str encode with a single byte scan (position/rposition + an
  ACGT count). The packed value was always discarded; only validity and base
  length were used. Kept bug-for-bug equivalent to from_umi_str (both cases,
  >32-bases-per-half rejection); prefix bytes before ':' are never validated so a
  'bb:' prefix is ignored, not rejected.
- Single-molecule fast path: decide the strand with a new allocation-free
  orientation_is_ab helper instead of building a reversed String per read.
  umi < reverse(umi) reduces to comparing the two dash-halves, since '-' is
  smaller than every byte that can appear in a half.
- matches_paired: compare reverse(lhs) to rhs via reversed_matches_within (a
  positional byte compare) instead of allocating the reversed string on every
  adjacency-graph comparison.
- upper_umis: borrow raw_umis directly when no UMI contains a lowercase byte (the
  common case), allocating an uppercased copy only for mixed-case input.
- Paired-format validation: locate the first '-' and confirm no second, instead
  of split('-').count().

Grouping output is unchanged: family-size histograms and molecule counts are
byte-identical before and after (agilent-hs2 paired: 10,220,662 molecules). On a
c7g.4xlarge at threads=0 this recovers roughly three-quarters of the paired
regression on agilent-hs2 (from +4% over the pre-regression baseline to ~+1%),
with the untouched identity strategy flat as a control. New rstest tables pin
each rewritten helper to the definition it replaces.
@nh13
nh13 force-pushed the nh/paired-assigner-allocs branch from 34ec4e8 to ece62be Compare July 23, 2026 22:21
@nh13
nh13 temporarily deployed to github-actions July 23, 2026 22:21 — with GitHub Actions Inactive
@nh13

nh13 commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13
nh13 merged commit c279c32 into main Jul 24, 2026
14 checks passed
@nh13
nh13 deleted the nh/paired-assigner-allocs branch July 24, 2026 15:41
@nh13 nh13 mentioned this pull request Jul 24, 2026

This branch was previously deployed

1 inactive deployment
github-actions — ece62be3 Deployed Jul 23, 2026 by nh13 via coverage #2990
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