Conversation
JESUSROYETH
requested review from
Borda,
SkalskiP,
isaacrob and
probicheaux
as code owners
September 3, 2026 09:00
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1419 +/- ##
=======================================
Coverage 86% 86%
=======================================
Files 114 114
Lines 15008 15009 +1
=======================================
+ Hits 12961 12962 +1
Misses 2047 2047 🚀 New features to boost your workflow:
|
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The focused optimization preserves eval-mode correctness and is adequately covered by regression tests.
Pull request overview
Optimizes unoptimized inference by avoiding redundant recursive eval-mode assignments while preserving mixed-mode correction.
Changes:
- Scan the module tree before calling
eval(). - Add regression coverage for already-evaluated and mixed-mode trees.
- Document measured inference improvements.
File summaries
| File | Description |
|---|---|
src/rfdetr/detr.py |
Conditionally applies eval() only when needed. |
tests/inference/test_predict_eval_mode.py |
Covers skipped assignments and mixed-mode restoration. |
CHANGELOG.md |
Records behavior and benchmark results. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Borda
approved these changes
Sep 3, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
RFDETR.predict()callsmodel.eval()on every unoptimised inference call.nn.Module.eval()just callstrain(False), and that reassignstrainingon every registered module throughnn.Module.__setattr__. It does this even when the whole tree is already in eval mode, so most calls pay a recursive walk that changes nothing.This keeps the same per-call check, but skips the recursive assignments when there is nothing to change:
The scan still covers the root and every registered submodule. So a replacement model left in training mode after
RFDETR.train()still triggers the existing recursiveeval()call, and so does a single submodule toggled directly under an eval-mode root. The optimised inference path still returns before this code, that part doesn't change.Why it is safe
The unconditional call was added on purpose in
d0740fd(#1146). Before that,predict()only entered eval mode while it was emitting its once-only warning, sopredict()→train()→predict()could leave dropout active on the second prediction. This change keeps that fix working:RFDETR.train()rebindsself.model.modelto the module Lightning returns, that replacement reports training mode, and the scan finds it and callseval()on it.I also considered checking only the root's
trainingflag, but that breaks a mixed-mode tree, for example when a child gets toggled directly. Scanning every module keeps the old behaviour and still skips the expensive recursive assignments for the common case, everything already in eval.On the RTX 4060, the read-only scan costs 0.18–0.22 ms for Nano/Small against 0.61–0.72 ms for the unconditional
eval(). So it saves 0.43–0.51 ms of host work on every call that finds the tree already in eval. A call that reaches a module still training pays that same recursiveeval()cost plus the scan on top, in that case it doesn't get faster.Results
Public
RFDETR.predict()on one real COCO image, pinned todevelop@ffc69fd. Each process reports the median of 60 timed calls after 30 warm-up ones; brackets are the full range across counterbalanced baseline/patched fresh processes.All six baseline/patched envelopes are disjoint. Every run gave the same detection count and the same SHA-256 fingerprint over
xyxy,confidenceandclass_idin both arms. The removed work is host-side Python, so how much you save in absolute terms depends on the host CPU and the torch build. Its share also depends on how much GPU work is left.Tests
develop(call(False)) and green with the patch.tests/inference/with the repo's pytest addopts and CPU marker selection: 300 passed, 5 skipped, 13 deselected.develop.gpu and not e2e_tensorrtsurface on the L4: 71 passed, including all 22 GPU benchmarks and all 17 pretrained COCO val2017 inference cases across detection, segmentation, keypoints,predict(), and PTL evaluation.Not covered