Skip to content

perf: replace vector-returning iteration methods with lazy ranges (P1 + A2)#36

Merged
csparker247 merged 3 commits intodevelopfrom
perf/p1-lazy-iteration
Mar 16, 2026
Merged

perf: replace vector-returning iteration methods with lazy ranges (P1 + A2)#36
csparker247 merged 3 commits intodevelopfrom
perf/p1-lazy-iteration

Conversation

@csparker247
Copy link
Copy Markdown
Member

@csparker247 csparker247 commented Mar 16, 2026

Summary

  • P1: Eliminates hot-path heap allocations in the ABF/ABFPlusPlus solver loops (~10+ vector allocations per solver iteration on a mesh with F faces and V_int interior vertices)
  • A2: Adds face->edges() named iterable accessor, resolving the awkward *face dereference pattern
  • P3: InitializeAnglesAndWeights already calls v->wheel() exactly once and stores the result; with P1's re-iterable Range<WheelIterator>, no allocation or redundancy remains

Changes

Method Before After
vertices() std::vector<VertPtr> (copy) const std::vector<VertPtr>&
faces() std::vector<FacePtr> (copy) const std::vector<FacePtr>&
edges() std::vector<EdgePtr> (built per call) Range<EdgesIterator> (lazy)
vertices_interior() std::vector<VertPtr> (filtered copy) Range<FilteringIterator<...>>
vertices_boundary() std::vector<VertPtr> (filtered copy) Range<FilteringIterator<...>>
Vertex::wheel() std::vector<EdgePtr> (built per call) Range<WheelIterator> (lazy)
Face::edges() (new) Range<FaceIterator>

New types (all in HalfEdgeMesh.hpp)

  • detail::Range<Iter> — lightweight begin/end wrapper supporting range-for, .empty(), .front()
  • detail::FilteringIterator<Iter, Pred> — input iterator skipping non-matching elements
  • WheelIterator<Const> — linked-list walk around a vertex (edge->pair->next chain), skipping boundary edges
  • EdgesIterator<Const> — lazily flattens all face edges across all faces

All existing call sites are syntactically unchanged (range-for, .size(), .num_*()) except AngleBasedLSCM::Compute which changes vertices_boundary()[0]vertices_boundary().front().

Kept as vector-returning (not hot path): outgoing_edges(), incoming_edges(), boundaries(), connected_components().

Test plan

  • 11 regression tests in TestHalfEdgeMesh.cpp: IterateVertices, IterateFaces, IterateEdges, IterateEdgesCount, IterateInteriorBoundaryPartition, IterateWheel, IterateWheelBoundaryVertex, IterateWheelReIterable, IterateNoInteriorVertices, RangeFrontEmpty, FaceEdges
  • All existing tests pass
  • All examples compile
  • git clang-format applied
  • Single-header amalgamation updated

Closes #4, Closes #14

🤖 Generated with Claude Code

csparker247 and others added 2 commits March 16, 2026 07:36
…, A2)

Eliminates hot-path heap allocations in the ABF/ABFPlusPlus solver loops by
replacing all vector-copying mesh iteration methods with zero-allocation
alternatives:

- vertices()/faces() now return const vector references (zero copy)
- edges() returns a lazy Range<EdgesIterator> flattening face→edge traversal
- vertices_interior()/vertices_boundary() return Range<FilteringIterator>
- Vertex::wheel() returns Range<WheelIterator> (linked-list iterator)
- Face::edges() added as a named iterable accessor, resolving A2

New iterator types (WheelIterator, EdgesIterator) are private nested classes
in HalfEdgeMesh modeled on the existing FaceIterator. detail::Range<Iter>
and detail::FilteringIterator<Iter,Pred> are added to the detail namespace.

Callers are syntactically unchanged except AngleBasedLSCM which changes
vertices_boundary()[0] to vertices_boundary().front().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- IterateEdgesCount: verify edges() yields exactly 3*num_faces() half-edges
- IterateWheelBoundaryVertex: wheel() on a boundary vertex terminates correctly
- IterateWheelReIterable: Range is re-iterable (two passes yield same results)
- IterateNoInteriorVertices: vertices_interior().empty() on a triangle mesh
- RangeFrontEmpty: Range::front() and Range::empty() correctness

Also removes (resolves A2) track reference from Face::edges() docstring.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All three resolved by PR #36 (perf/p1-lazy-iteration).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@csparker247 csparker247 merged commit 0e3c3b8 into develop Mar 16, 2026
7 checks passed
@csparker247 csparker247 deleted the perf/p1-lazy-iteration branch March 16, 2026 12:53
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.

[P3] InitializeAnglesAndWeights calls wheel() redundantly (HalfEdgeMesh) Improve interface for iteration

1 participant