Where the performance comes from#
This page explains the shape of the numbers on the cards: where eagle is ahead, where it ties, and why. Each section states a mechanism and then shows the card figures that bear on it. The numbers come from the committed performance cards and from the large-N compare evidence; none is typed by hand. Unless a sentence names another device it is about the NVIDIA H100 card, RK4 oscillators, N = 10⁶ samples, up to 1000 steps each.
Summary#
eagle is ahead where samples finish at different times, because it does not spend GPU lanes on samples that are already finished. It ties where every sample runs the same number of steps, because both eagle and NVIDIA Warp’s per-thread kernel then sit at the GPU’s FP64 limit and there is nothing left to recover. The sections below take the mechanisms one at a time.
Lanes stay on live samples#
In a per-thread kernel each lane owns one sample for the whole run. When that sample stops, its lane keeps its place in the warp until the slowest sample in the same warp stops, so a thinning batch costs nearly as much as a full one. eagle’s persistent path launches once, sized to what the device can keep resident, and each lane takes the next unfinished sample from a shared counter when its own is done. The measure of the difference is useful work per lane: more of the lanes executing each instruction are on live samples. It is not a claim about fewer stalls; issue slots are about equally busy in both kernels.
The cards show the effect as the gap between the two distributions. Going from the uniform batch to the spread batch (samples stopping at their own step counts, so far fewer sample-steps in total), warp_kernel takes 0.90× of its uniform time, while eagle.simulate takes 0.34×. The per-thread kernel does almost the same amount of lane-time on either shape; eagle’s time follows the work that is left. On the spread batch warp_kernel takes 1.54 ms against eagle.simulate’s 613 µs.
No per-step launch cost#
Launching one kernel per step costs microseconds multiplied by the number of steps, and it is paid whether the batch holds a thousand samples or a million. The automatic policy avoids it by fusing several steps into one launch or by keeping one kernel resident until the batch is done. The card’s own per-step loop, which launches once per step from the host, shows the cost: at N = 10⁶ on spread-1000 it takes 32.3 ms on the H100 against 613 µs for eagle.simulate, and 723 ms against 150 ms on the P2000.
The same cost matters more the faster the GPU, because the compute it sits next to shrinks while the launch cost does not: the per-step loop is 4.8× eagle.simulate on the P2000 and 52× on the H100.
When compaction and reorder pay off#
Compaction packs the live samples into the first warps, and the reorder sorts them so that samples with similar remaining work share a warp. Both cost extra launches and passes over the batch. On the smaller cards the saved lane time is worth more than that cost: on the P2000 at N = 10⁶ on spread-1000 the plain graph takes 586 ms and the compaction-plus-reorder arm 253 ms; on the T4 the figures are 173 ms and 113 ms. On the A100 and the H100 the same arm loses: 5.33 ms against 17.2 ms on the A100 and 2.13 ms against 10.7 ms on the H100, because those devices finish the batch before the bookkeeping has anything to recover.
The automatic policy chooses between these arms for the device and the batch, so a caller does not have to know which side of this line the GPU falls on.
Scaling with N#
At small N every tool sits on a floor set by the longest sample’s chain of steps. It is not a launch cost: on the H100 at N = 10³ the automatic arm takes 42.4 µs when samples run up to 100 steps and 94.9 µs when they run up to 1000, so the floor grows with the step count. At N = 10⁴ the two tools are level (warp_kernel takes 0.98× of eagle.simulate’s time).
Beyond that the gap opens as there is more work per lane to recover. On the H100, warp_kernel takes 2.5× eagle.simulate’s time at N = 10⁶ on the card, 3.1× at N = 10⁷ and 3.2× at N = 10⁸ (compare evidence: one session per device, no repetitions rule, so a measurement at load rather than a certified figure). A million samples is a light load for this device, and the gap keeps growing as the batch grows.
Across GPUs, against NVIDIA Warp#
At N = 10⁷ on spread-1000, with walls normalised to a common SM clock, warp_kernel takes 3.7× the time of the automatic arm on the P2000, 3.9× on the T4, 3.5× on the A100 and 3.1× on the H100. The gap is larger on the smaller GPUs. The per-thread kernel pays about the same relative dead-lane cost on every device: its spread time is 0.88× of its uniform time on the P2000 and 0.90× on the H100. What varies from card to card is how much of eagle’s own throughput survives a spread batch.
This trend holds against Warp only. Against array-style tools (JAX, CuPy) the gap grows on bigger GPUs, for a different reason: they make whole-array passes over finished samples, so their cost follows the batch size rather than the work that is left.
Efficiency on uneven batches#
A tool’s efficiency on an uneven batch is its sample-steps per second on the spread batch (N = 10⁶, up to 1000 steps; for eagle the default eagle.simulate, which picks the launch mode itself, and for Warp its fastest arm) divided by the rate the device would reach if every FP64 pipe issued the step’s 22 instructions back to back (16 DFMA, 4 DMUL, 2 DADD). The rows below are that ratio, floored to a whole percent, for eagle and NVIDIA Warp’s per-thread kernel on each GPU card.
GPU |
|
NVIDIA Warp |
|---|---|---|
P2000 |
92% |
22% |
T4 |
81% |
19% |
L4 |
79% |
19% |
A100 |
65% |
16% |
H100 |
46% |
18% |
RTX PRO 6000 |
70% |
18% |
100 % is the device’s top FP64 speed: every FP64 unit issuing the step’s 22 instructions back to back at the clock the device ran at, measured with a register-only FMA probe where one is available and otherwise taken from the published FP64 rate at the clock recorded during the run. Nothing can exceed it, and a tool that keeps paying for finished samples can keep at most the live share of the batch (21.6 % of the sample-steps here). The reference is the same for every tool and does not depend on any of them. Read the figures as where each tool sits between the live work and the device’s limit on this one batch shape; the per-thread kernel’s lower figure on spread batches is the dead-lane cost described above, not a statement about its dense-batch speed, where it sits at the same limit as eagle.
Where eagle does not win#
On the dense uniform batch, where every sample runs every step, there is nothing to reclaim and the two tools tie. On the H100 at N = 10⁶ warp_kernel takes 1.71 ms and eagle.simulate 1.75 ms; at N = 10⁷ the compare evidence has 15.9 ms for Warp and 16.8 ms for the automatic arm. Warp is the better fit for that shape.
Cold compile is the other place: JAX’s first call takes 242 ms against eagle.simulate’s 875 ms, and 40 ms against 70 ms once both are warm.
What is not shown yet#
The H100 gap at large N stays smaller than the smaller cards’ gap. There is a hypothesis for it, but no profiler evidence yet, so it is not stated here as a result; it is the subject of a study in a future release.