Skip to content

perf(mcbyte): skip mask-only work without mask evidence - #573

Open
JESUSROYETH wants to merge 2 commits into
roboflow:developfrom
JESUSROYETH:perf/mcbyte-no-mask-association
Open

perf(mcbyte): skip mask-only work without mask evidence#573
JESUSROYETH wants to merge 2 commits into
roboflow:developfrom
JESUSROYETH:perf/mcbyte-no-mask-association

Conversation

@JESUSROYETH

@JESUSROYETH JESUSROYETH commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

McByte defaults to enable_mask_manager=False, so the mask-disabled path is the common one, not an edge case. Its three association stages still ran into mask-only candidate construction, though: condition_similarity_with_masks() copied and reduced the full similarity matrix, built the ambiguity and optional isolated-candidate matrices, and only after that checked whether there was usable mask evidence. On a default tracker those matrices got built for nothing, then discarded.

The fix

The function keeps the same order as before: input validation, clear-match locking, reduced assignment. It now returns right there, before mask-only candidate construction starts, when no tracklet can actually receive mask evidence. That covers a few cases: the output or mask array is missing, the mask array has zero length, or the tracklet-to-mask or confidence mapping is empty or missing. With usable mask evidence, candidate construction and score updates stay the same as before.

Measured

benchmark before after delta
microbench (µs/call, 1,091 matrices, MOT17-04 FRCNN, median [range], n=11) 21.090 [20.832, 21.315] 12.709 [12.603, 12.850] -39.74%
DanceTrack e2e (ms, 25 seq / 25,508 frames / 225,148 boxes, median [range], 7 pairs) 10,224.90 [10,046.24, 10,669.01] 9,780.26 [9,600.19, 10,371.11] -4.35%
MOT17-04 e2e (ms, median [range], 7 pairs) 531.68 [511.18, 550.79] 507.51 [499.67, 537.14] -4.55%

For each dataset five of seven pairs came out faster and two slower. The ranges overlap, so I'd call the end-to-end numbers supportive but machine-dependent, not a clean win.

Correctness

5,000 random mask/no-mask cases give the exact same bit-level digest before and after, with zero mutations of the caller's matrix. I also compared a real MOT17-04 run, pairing 15,161 boxes by IoU (not by index) between the two implementations: minimum IoU 1.0, zero coordinate-bit or tracker-ID mismatches.

Tests

A regression test covers the six no-evidence states, checks the candidate builders don't get called, and verifies both the locked/reduced result and that the caller's matrix stays untouched. Mask-association suite: 40 passed. Full non-integration suite: 1,554 passed, 3 skipped, 14 deselected. Pre-commit clean (ruff, formatting, codespell, mypy).

@JESUSROYETH
JESUSROYETH requested a review from SkalskiP as a code owner August 20, 2026 00:38
@Borda
Borda requested a balanced review from Copilot August 20, 2026 10:37
@Borda Borda added the enhancement New feature or request label Aug 20, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Optimizes McByte’s common mask-disabled association path by skipping unnecessary mask candidate construction.

Changes:

  • Adds an early return when usable mask evidence is unavailable.
  • Refactors mask-boost inputs and adds regression coverage.
  • Documents the performance improvement.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/trackers/core/mcbyte/mask_association.py Skips mask-only candidate work when evidence is unavailable.
tests/core/test_mcbyte_mask_association.py Tests six no-evidence states and input immutability.
CHANGELOG.md Records the optimization.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@Borda

Borda commented Aug 20, 2026

Copy link
Copy Markdown
Member

CPU microbenchmark over 1,091 association matrices captured from real MOT17-04 FRCNN detections, 11 repetitions: 21.090 µs/call median [20.832, 21.315] down to 12.709 [12.603, 12.850], -39.74%, disjoint ranges.

End-to-end, I ran seven alternating fresh-process pairs on each dataset. DanceTrack (25 sequences, 25,508 frames, 225,148 boxes) went from 10,224.90 ms median [10,046.24, 10,669.01] to 9,780.26 [9,600.19, 10,371.11], -4.35%. MOT17-04 went from 531.68 ms [511.18, 550.79] to 507.51 [499.67, 537.14], -4.55%. For each dataset five of seven pairs came out faster and two slower. The ranges overlap, so I'd call the end-to-end numbers supportive but machine-dependent, not a clean win.

This looks good; let's re-format it as a table and ensure that the HOMTA/MOTA metrics do not regress

@JESUSROYETH

Copy link
Copy Markdown
Contributor Author

mcbyte isn't in ALL_TRACKER_IDS, so there's no existing pinned-reference test for it here.. I ran the same method by hand: feed ground-truth boxes into McByteTracker(enable_cmc=False, enable_mask_manager=False), write MOT files, run them through evaluate_mot_sequences (the repo's own TrackEval-parity evaluator) on DanceTrack (25 seq) and SportsMOT (45 seq).

dataset HOTA MOTA IDF1 IDSW
DanceTrack (25 seq) 81.21 99.63 78.31 587
SportsMOT (45 seq) 86.45 99.17 81.79 721

Ran it twice in the same tree, mask_association.py stashed back to develop@35dd78b for baseline, patch applied for the second run, nothing else changed. Every value above came out identical to the decimal. That's expected and not a coincidence, the Correctness section already showed the output digest is bit-identical over 5,000 cases with zero mismatches on the MOT17-04 IoU pairing, so any metric computed on that output has to come out the same. This just makes it explicit with the metrics instead of leaving it as an inference.

Updated the Measured section to a table too.

@Borda
Borda requested a review from tstanczyk95 August 21, 2026 10:24
Comment thread src/trackers/core/mcbyte/mask_association.py Outdated
Comment thread src/trackers/core/mcbyte/mask_association.py Outdated
Comment thread src/trackers/core/mcbyte/mask_association.py Outdated

@tstanczyk95 tstanczyk95 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.

Hi @JESUSROYETH! 👋

Thank you for your valuable contribution! It overall looks very good!

I thoroguly checked your PR and I have some remarks. Here below and in the code. Could you reply to and/or resolve all of them? Thanks!

Remarks:

1.

the mask-disabled path is the common one, not an edge case

Just for clarity, this is the default setting as the heavy mask dependencies are not installed with pip install trackers (one needs to perform pip install trackers[mask] to install the mask support).

Normally though, McByte needs the mask for its desired behavior. Otherwise, it behaves similarly to BoT-SORT.

2.

I ran the same method by hand: feed ground-truth boxes into McByteTracker(enable_cmc=False, enable_mask_manager=False)

So if I understand, you re-run McByte with your changes applied, but only without the mask enabled? Do you have the results with masks enabled? I mean HOTA, IDF1 and MOTA. We also need to see these before proceeding. Notably, the difference before and after your changes.

3.

“I ran the same method by hand: feed ground-truth boxes into McByteTracker(enable_cmc=False, enable_mask_manager=False)

What do you mean by ground-truth boxes specifically? The track ground truth data without track IDs? Or actual detections? Normally, we prefer the result comparison with detections, e.g. coming from YOLOX as used for our benchmarks on test splits. Detections are imperfect and thus can expose more performance differences when modifying the tracker's code, while ground truth bounding boxes are idealized and might not exposed as much.

4.

Every value above came out identical to the decimal.

That's very good and well appreciated. Please keep doing it 🙂

…onstruction

Addresses review feedback on the mask-only skip.

_apply_mask_similarity_boosts takes MaskOutput again instead of the three
unpacked fields, and narrows the two optional ones (masks, mask_avg_prob_dict)
inside the helper. tracklet_mask_dict is not optional, so it never needed
unpacking.

condition_similarity_with_masks no longer builds MaskConditionedAssociation
before the boosts run. The no-evidence path builds and returns its own result,
and the mask path builds it after the boosts have been applied, so the in-place
mutation happens on a plain local array rather than through an already
constructed frozen dataclass.

Output is unchanged: both paths compared bit-identical against the previous
revision over 5000 randomised cases, with the caller matrix untouched.
@JESUSROYETH

Copy link
Copy Markdown
Contributor Author

Hi @tstanczyk95 👋

Thank you very much for the review, it was really helpful. The points about how to frame the change and how to validate it are fair, and I will keep this pattern in mind for the next PRs.

On 1, you are right and my wording was loose. Saying "the mask-disabled path is the common one" is true about a plain pip install trackers, but written like that it reads as if running McByte without masks were the normal way to use it, and it is not. The masks are the point of McByte, without them it ends up close to BoT-SORT. What the patch skips is work on a path that cannot produce any mask evidence, that is not an argument for running it that way.

On 3, by ground-truth boxes I meant the annotation boxes fed in as if they were detections, with the track IDs dropped. Masks were off in that run too. Your objection is correct: that makes it an oracle-detection setup, and therefore less sensitive to association changes than imperfect detector output. It was not a strong enough validation for the concern you raised.

On 2, I ran it again the way you asked, with masks enabled and real detections, using the repo's own benchmark instead of a script of mine: trackers benchmark mcbyte --dataset=[mot17] --device=cuda, which sets enable_mask_manager=True and runs SAM + Cutie on GPU. Data is MOT17-val from download_dataset, 7 sequences and 2652 frames, with the public FRCNN detections that ship with it (30711 boxes), converted to the xyxy layout the CLI expects. Baseline is 35dd78b, the merge-base of this PR, and after is 55bc82f, so the diff between the two arms is only the two commits here. Both arms run from the same virtualenv with PYTHONPATH pointed at a different worktree, so the dependency versions are identical.

To check run-to-run stability, I ran the baseline twice. Both repetitions matched at full metric precision and produced the same seven SHA-256 hashes. The PR run then produced those same hashes.

metric baseline 35dd78b PR 55bc82f delta
HOTA 50.732 50.732 0.000
IDF1 57.421 57.421 0.000
MOTA 47.705 47.705 0.000
IDSW 167 167 0

All seven per-sequence results also matched, and the seven MOTChallenge output files were byte-identical. COMBINED HOTA is 0.5073214955745664 in both runs if you want the full precision.

Per-sequence, identical on both arms
sequence HOTA IDF1 MOTA IDSW
MOT17-02 35.945 39.020 30.273 24
MOT17-04 54.958 61.676 48.792 16
MOT17-05 45.754 54.895 52.011 16
MOT17-09 52.443 59.474 51.650 7
MOT17-10 51.372 59.523 54.398 61
MOT17-11 49.768 57.002 56.210 26
MOT17-13 58.782 71.583 61.027 17

One thing worth stating, since an all-equal table can also mean the mask path never ran: I instrumented condition_similarity_with_masks on MOT17-09 to count how often it was actually reached with mask evidence. Out of 536 calls, 530 had usable mask evidence and the other 6 exercised the no-evidence path. In 196 calls at least one boost landed, modifying 498 association scores in total. So both the mask-conditioned path and the new no-evidence shortcut were exercised.

I should be clear about one substitution: these are the public FRCNN detections the repo hosts for MOT17-val, not YOLOX. The YOLOX numbers you use are on the test splits through Codabench, which I cannot score locally, and I did not find YOLOX detection files for the val split in the repo. These FRCNN detections still exercise the concern you raised: imperfect detections with misses and false positives instead of idealised boxes, and anyone can reproduce it with one download_dataset call. If you have the YOLOX detection files you use for the benchmarks, I am happy to run the same comparison on them.

Also, the absolute values above are not comparable with the published MOT17 table, they use a different split and a different detector. They are only meant for the before/after comparison.

@tstanczyk95
tstanczyk95 self-requested a review August 25, 2026 12:28

@tstanczyk95 tstanczyk95 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.

Hi @JESUSROYETH,

Great, thank you for your detailed answer and explanations. It is well appreciated!

@Borda It looks good to me now, it can be merged.

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants