Repository navigation
Conversation
The move constructor (and move assignment) of sharing_treet (the base of irept, and thus exprt, typet, codet, source_locationt, ...), dstringt, and memory_sizet only copy or swap scalar members/pointers and therefore cannot throw, but were not marked noexcept. Because they were not noexcept, std::is_nothrow_move_constructible was false for these types, which forced std::vector (and other standard containers) to copy rather than move elements when reallocating. For irept-derived types a copy is expensive: it bumps reference counts across the whole shared subtree and contends on the global string container. Since irept::subt is std::vector<irept> and exprt::operandst is std::vector<exprt>, this affects hot paths throughout CBMC. Marking these moves noexcept lets containers relocate by moving. In an isolated microbenchmark on std::vector<exprt> this gave a 2.8x speedup for pre-reserved reallocation and up to 6.5x for relocation-heavy workloads.
peterschrammel
approved these changes
Oct 3, 2026
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #9174 +/- ##
========================================
Coverage 80.85% 80.85%
========================================
Files 1717 1718 +1
Lines 190161 190166 +5
Branches 73 73
========================================
+ Hits 153759 153767 +8
+ Misses 36402 36399 -3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This branch has not been deployed
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.
Summary
Mark the move constructor (and move assignment) of a few core
utildatastructures
noexcept, so thatstd::vectorand other standard containersmove elements instead of copying them on reallocation.
The affected moves only copy or swap scalar members / a single pointer and
therefore cannot throw, but they were not marked
noexcept:sharing_treet(irep.h) — the base ofirept, and thus ofexprt,typet,codet,source_locationt,symbol_exprt, … None of these adddata members, so the fix applies to the whole hierarchy.
dstringt(dstring.h) —irep_idt; a member of everytree_nodet.memory_sizet(memory_units.h/.cpp) — the move assignment was alreadynoexcept; the move constructor was not (inconsistent).Why it matters
Because these moves were not
noexcept,std::is_nothrow_move_constructiblewas
false, which forcesstd::vectorto copy rather than move whenit reallocates. For
irept-derived types a copy is expensive: it bumpsreference counts across the whole shared subtree and contends on the global
string container.
This is a hot path:
irept::subtisstd::vector<irept>andexprt::operandstisstd::vector<exprt>, both grown viapush_backthroughout goto-conversion, simplification, and symbolic execution.
Verification
std::is_nothrow_move_constructible/..._assignablefor the affectedtypes, before vs. after (1 = container will move, 0 = container will copy):
ireptexprttypetsource_locationtdstringtBenchmark
Microbenchmark on
std::vector<exprt>(each element has 4 operands + types,so copies incur real ref-count traffic). Identical source compiled twice —
against the fixed headers (
noexceptmoves) and against reverted copies (nonoexcept) — linked against the sameutil.a, built withclang++ -O2 -std=c++17. Best of 5 runs.reserve(N = 1M)shrink_to_fit, N = 1M)The benefit scales with how often vectors relocate; relocation-heavy
workloads benefit the most. The probe confirmed the mechanism:
is_nothrow_move_constructible<exprt>was1in the ON build and0in theOFF build — the only difference being whether the vector moves or copies.
This is an isolated microbenchmark of the vector-growth path, not an
end-to-end CBMC run; real-world impact depends on how much time is spent
growing/relocating these vectors.
Risk
Zero correctness risk: all affected move operations provably cannot throw
(pointer copy/swap, or copying a
std::size_t/unsigned), so thenoexceptspecification is accurate.