Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Performance methodology

Benchmarks use release mode and Criterion. They contain no file or console I/O inside the measured loop. Raw Criterion state is build output under target/ and is not committed.

An orientation run on 2026-07-25 used Rust 1.97.1 on an AMD Ryzen 9 9950X under Linux 6.8.0-136. One Criterion process reported:

Foundation workloadMedian estimate
stumpff_c(1e-12)1.709 ns
stumpff_s(-4)9.437 ns
jd_to_mjd20000.240 ns
three-vector cross product4.794 ns
elliptic mean → eccentric, e = 0.99989.51 ns
hyperbolic mean → anomaly, e = 1.5118.3 ns
64 elliptic conversions, e = 0.95.857 µs
classical → Cartesian40.25 ns
Cartesian → modified equinoctial29.33 ns
Cartesian → equinoctial Jacobian151.7 ns
64 classical → Cartesian conversions2.575 µs

The Phase 5 orientation run used identical scalar inputs in Rust and the pinned C++ oracle, with both built in release mode on the same machine:

Propagation workloadRust Criterion medianC++ elapsed average
Lagrange, elliptic146.24 ns149.263 ns
Lagrange, hyperbolic567.50 ns450.166 ns
Universal variables, elliptic250.05 ns260.613 ns
Lagrange propagation + STM216.68 ns444.289 ns
1,024 Lagrange calls113.78 µs (9.00 million/s)

Mission-design kernels

Phase 6 measured each operation separately in the same Rust orientation run:

WorkloadRust median
Hohmann transfer6.258 ns
Flyby constraints10.923 ns
Flyby delta-v34.567 ns
Zero-revolution Lambert problem235.76 ns
Seven-solution multi-revolution Lambert problem1.263 µs

Ephemerides

Scalar and ordered-batch measurements were kept separate:

PhaseProvider and workloadResult
7Keplerian, one epoch80.299 ns
7Keplerian, 256 ordered epochs22.575 µs
8JPL low-precision Earth, one epoch95.198 ns
8JPL low-precision Earth, 256 ordered epochs31.611 µs
9VSOP2013 default-threshold initialization11.615 µs
9VSOP2013 default-threshold scalar state339.98 ns
9VSOP2013 default-threshold 256-state batch89.554 µs
9VSOP2013 1e-9 scalar state37.157 µs

The matching C++/heyoka VSOP2013 harness measured first-time JIT costs of 63.96 ms at 1e-5 and 552.53 ms at 1e-9. Warm scalar calls took 166 ns and 5.908 µs, respectively. Enabling VSOP2013 increased the Rust release benchmark executable from 2.6 MiB to 7.0 MiB. ADR 0003 explains the data and cache decision.

Integration and dynamics

DOP853 selection

The Phase 10 decision harness produced these orientation measurements:

WorkloadSelected Rust facadeWarmed C++/heyoka
Representative nominal Kepler solve11.865 µs3.722 µs
State plus 6 by 6 STM85.663 µs84.440 µs

Cloning the cached C++ nominal integrator once cost 0.467 ms. In a same-profile candidate test, the selected facade took 15.431 µs and ode_solvers took 7.369 µs. The latter lacked the required root and sensitivity facilities and allocated per step, so nominal timing alone did not decide the dependency. ADR 0004 records the configuration, ranges, and risks.

Taylor integration

The fixed-system Taylor backend uses a matched-accuracy protocol because coefficient sweeps are not comparable to DOP853 right-hand-side calls. For one eccentric nondimensional revolution, Taylor’s speed relative to DOP853 was:

ToleranceTaylor/DOP853 speed
1e-90.87×
1e-121.18×
1e-141.61×
Machine epsilon2.16×

Taylor produced smaller final-state errors at every point. This is deliberately a narrow conclusion: Taylor is a high-accuracy option, not a low-accuracy replacement. The committed CSV also contains the 100- and 1,000-revolution rows; see High-accuracy Taylor integration.

All eleven built-in Taylor models now use incremental coefficient evaluators. Across the representative eight-model migration benchmark, replacing repeated full-series evaluation reduced warmed end-to-end propagation time by 6.45× to 28.97×. The linked Taylor guide gives per-model results and validation limits.

Evaluated models and controlled dynamics

PhaseWorkloadRustWarmed C++/heyoka
11Kepler RHS10.828 ns
11CR3BP RHS32.833 ns
11BCP RHS63.386 ns
11CR3BP propagation7.808 µs2.885 µs
11CR3BP state plus STM45.368 µs95.017 µs
12ZOH Kepler RHS8.928 ns
1232-segment alternating-control schedule36.089 µs10.268 µs
13Cartesian mass-optimal RHS52.384 ns
13Cartesian mass-optimal propagation142.52 µs11.716 µs

The Phase 11 comparisons use the same initial state, final time, parameter, and 1e-12 tolerance.

The Phase 12 Rust schedule deliberately starts an independent DOP853 solve at each switch. Its timing includes all 32 restarts and confirms that integration does not perform a segment-count-dependent control search.

The Phase 13 propagation covers 1.2345 normalized time units. Rust uses a 0.01 maximum step to meet the recorded oracle tolerance, whereas the C++ Taylor solve has no equivalent limit. Treat this as an identified performance target, not as a like-for-like algorithm comparison.

Low-thrust legs

PhaseWorkloadRustWarmed C++/heyoka
14Five-segment Sims–Flanagan mismatch1.002 µs1.037 µs
14Complete analytic mismatch Jacobian4.241 µs12.748 µs
1520-segment normalized Kepler ZOH mismatch23.67 µs6.865 µs
15Complete endpoint/control/time-grid Jacobian493.55 µs182.17 µs

The Phase 14 comparison uses the same endpoints, masses, controls, duration, propulsion parameters, gravity parameter, and cut = 0.6. Construction and validation are outside both timings.

The Phase 15 comparison uses the same states, chronological controls, time grid, constants, cut, and 1e-12 tolerance, with no maximum step. Rust integrates every segment independently and uses fixed-size numerical dynamics Jacobians. Both operations therefore remain visible optimization targets.

Python batch throughput

The Phase 16 release-wheel harness used 20,000 items and the median of nine samples:

WorkloadPython scalar loopNumPy batchBatch improvement
stumpff_c38.9 ns/item14.6 ns/item2.67×
Lagrange propagation0.72 µs/item0.09 µs/item7.82×

These measurements include Python input/output conversion; they are not Rust-core timings. They show why throughput-sensitive Python code should use the explicit batch APIs.

Interpreting the results

These are not cross-language speed claims. CPU frequency was not fixed and the run is not a substitute for distributions collected under controlled affinity and power settings. The Julian arithmetic result is small enough that compiler optimization and timer resolution dominate its interpretation. The C++ column is an elapsed average from five million calls rather than a Criterion distribution; it is included as the required same-input orientation baseline, not as a statistically controlled language comparison.

Reproducing the measurements

Run the maintained harness with:

cargo bench -p pykep-core --bench foundation
cargo bench -p pykep-core --bench elements
cargo bench -p pykep-core --bench propagation
cargo bench -p pykep-core --bench mission
cargo bench -p pykep-core --bench integration
cargo bench -p pykep-core --bench dynamics
cargo bench -p pykep-core --bench legs
python python/benchmarks/wrapper_overhead.py
cargo run --release -p pykep-lambert-optimization-benchmark
cargo run --release -p pykep-taylor-benchmark

Each benchmark group has a distinct scope:

GroupWorkloads
FoundationArithmetic kernels plus scalar elliptic/hyperbolic anomaly solvers and a 64-value loop
ElementsScalar classical/equinoctial conversions, analytic Jacobians, and a 64-state loop
PropagationElliptic/hyperbolic Lagrange coefficients, universal variables, analytic STMs, and a 1,024-state loop
MissionTransfers, flyby constraints/delta-v, Lambert branches, and scalar/batch ephemerides
IntegrationNominal six-state DOP853 and Taylor propagation plus STM paths
DynamicsEvaluated right-hand sides, CR3BP nominal/variational propagation, ZOH schedules, and Pontryagin propagation
LegsSims–Flanagan mismatch/analytic gradients and generic ZOH mismatch/sensitivities

The foundation anomaly loop keeps branch-heavy iterative work separate from arithmetic kernels. Scalar propagation and STM APIs use fixed-size arrays and perform no heap allocation. Ephemeris measurements distinguish initialization, scalar, high-precision, and batch paths. The integration final-state callback does not retain internal steps.

Standalone Lambert optimization

The standalone Lambert optimization benchmark ports the fixed easy.kttsp leg from pykep-lambert. It measures deterministic objective throughput and then applies the native fcmaes-core CMA-ES and BiteOpt implementations to wait time and time of flight. Its source revision, physical constants, decision bounds, penalty, optimizer budget, and seed are printed with every run; see tools/lambert-optimization-benchmark/README.md for the complete protocol.

Cross-language comparison policy

C++ comparisons are added only when both sides execute identical input data, validation policy, branch families, tolerances, and output work. Initialization and batch throughput are reported separately from warm scalar latency.

Release stabilization evidence

Phase 18 adds a protocol-matched 100-sample Rust/C++ distribution, bootstrap median confidence intervals, CI regression limits, allocation/cache/ vectorization profiles, and five-point Python batch scaling. Full results and environment limitations are in stabilization.md.