Skip to content

perf(system): eliminate two Planetesimal clones in planetesimals_inte…#21

Merged
LeonidGrr merged 3 commits into
LeonidGrr:masterfrom
jzmiller1:fix/perf-3a-roche-scalars
May 13, 2026
Merged

perf(system): eliminate two Planetesimal clones in planetesimals_inte…#21
LeonidGrr merged 3 commits into
LeonidGrr:masterfrom
jzmiller1:fix/perf-3a-roche-scalars

Conversation

@jzmiller1

@jzmiller1 jzmiller1 commented May 11, 2026

Copy link
Copy Markdown
Contributor

Problem

planetesimals_intersect clones both p and prev_p purely to satisfy the borrow checker. Only three scalar fields (mass, mass, radius) are ever read from the clones, to compute the Roche limit. Planetesimal carries Vec<Planetesimal> moons plus Vec<Ring> rings, so each clone is a deep tree copy of every captured satellite. In the post-accretion bombardment loop these clones happen per-iteration and they consumed once and dropped.

This was caught while profiling a ~2.56M-system Rayon batch on a 12-core machine. The simulation completed correctly, but the moon-tree clones were a measurable share of the per-system cost.

Change

Replace the two clones in planetesimals_intersect with direct scalar extraction:

let (larger_mass, smaller_mass, smaller_radius) = if p.mass >= prev_p.mass {
    (p.mass, prev_p.mass, prev_p.radius)
} else {
    (prev_p.mass, p.mass, p.radius)
};
let roche_limit = roche_limit_au(&larger_mass, &smaller_mass, &smaller_radius);

Note the third argument: the original code passed &smaller.radius, matching roche_limit_au's documented contract that the third arg is the moon (smaller) body's radius. The refactor preserves this. Using smaller_radius keeps the physics bit-identical.

For the capture_moon call site (which previously passed &larger, &smaller), use std::mem::swap with a >= tiebreak to orient the bodies in place: after the swap, prev_p holds the larger body. This matches the original match p.mass >= prev_p.mass { ... } ordering exactly (p wins on equal mass).

A new reproducibility test (same_seed_same_system_after_fix_3a) is added to lock in that two successive runs at a fixed seed produce identical Debug-formatted output.

Impact

  • Zero behavioral change. Same Roche-limit value, same tiebreak. All 16 fixture tests pass bit-identically — no fixture regeneration needed.
  • Performance: clone-elimination in the post-accretion hot loop. A private fork validated against 2.56M stellar systems attributed roughly a 15% improvement (1362s → 1159s) to this fix in isolation. That number is reference-fork data, not re-measured for this exact patch; the structural cost being eliminated is the same.
  • Public API surface unchanged. No traits, public types, or function signatures altered. Single private-function refactor.

…rsect

The Roche-limit computation in planetesimals_intersect cloned both `p`
and `prev_p` purely to satisfy the borrow checker — only three scalar
fields (mass, mass, radius) are read from the clones. Planetesimal
carries Vec<Planetesimal> moons + Vec<Ring> rings, so each clone is a
deep tree copy. In hot post-accretion loops this is per-iteration waste.

Replace the two clones with direct scalar extraction:

    let (larger_mass, smaller_mass, smaller_radius) = if p.mass >= prev_p.mass {
        (p.mass, prev_p.mass, prev_p.radius)
    } else {
        (prev_p.mass, p.mass, p.radius)
    };

Note the third argument: the original code passed `&smaller.radius`
(the smaller body's radius, matching roche_limit_au's documented
contract that the third arg is the moon/smaller body's radius). The
refactor preserves this — using smaller_radius rather than the larger
body's radius keeps the physics identical.

For the capture_moon call site, use std::mem::swap with a `>=` tiebreak
to orient the bodies in place: after the swap, prev_p holds the larger
body. This matches the original `match p.mass >= prev_p.mass { ... }`
ordering exactly (p wins on equal mass).

All 16 fixture tests remain bit-identical after the refactor — zero
fixture drift under cargo test, as the clone-elimination itself does
not change the arithmetic order.
jzmiller1 added 2 commits May 12, 2026 09:27
# Conflicts:
#	src/structs/system.rs
The merge of master (post PR 1 + PR 2) changed events_log from
&mut AccreteEvents to Option<&mut AccreteEvents>. The capture_moon
call site at planetesimals_intersect was missed during conflict
resolution and moved the Option instead of reborrowing it, leaving
the subsequent coalesce_planetesimals and moons_to_rings calls
trying to use a moved value.

Add .as_deref_mut() to match the surrounding calls.
@LeonidGrr LeonidGrr merged commit b1eb511 into LeonidGrr:master May 13, 2026
6 checks passed
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.

2 participants