perf: parse fractional byte sizes exactly#20
Conversation
Signed-off-by: tison <wander4096@gmail.com>
Signed-off-by: tison <wander4096@gmail.com>
Benchmark comparison:
|
| Case | main median |
PR median | Paired change | Paired change IQR |
|---|---|---|---|---|
plain |
4.584 ns | 4.253 ns | −9.7% | −19.2% to −4.5% |
decimal-unit |
4.910 ns | 4.623 ns | −3.0% | −15.2% to +3.2% |
binary-unit |
4.571 ns | 4.464 ns | −5.2% | −6.6% to +4.3% |
fraction |
7.536 ns | 6.029 ns | −22.8% | −24.4% to −17.3% |
small-fraction |
9.793 ns | 7.856 ns | −19.5% | −25.1% to −13.0% |
grouped |
10.760 ns | 10.380 ns | −4.2% | −9.2% to −0.6% |
u64-max |
18.330 ns | 17.055 ns | −8.7% | −14.3% to −3.5% |
high-precision-decimal |
30.230 ns | 32.635 ns | +7.5% | +4.9% to +9.7% |
high-precision-binary |
41.605 ns | 122.000 ns | +197.4% | +187.7% to +221.7% |
malformed |
3.004 ns | 2.752 ns | −8.6% | −10.8% to −5.1% |
The result is favorable on the normal paths: ordinary fractional inputs improve by about 20–23%, while plain, grouped, maximum-integer, and malformed inputs improve by about 4–10%. The small changes for the two simple unit cases cross zero in the paired IQR, so I would treat them as noise rather than claim a win.
The two high-precision cases do more semantically correct work in the PR, so their timings are not like-for-like regressions:
- For
1.84467440737095516145 EB,maindouble-rounds to1844674407370955162; the PR returns the correct1844674407370955161. - For the long
...3125 EiBhalf-byte boundary,mainexits withErr(Malformed); the PR consumes the full fraction and correctly returnsOk(1). The roughly 3× cost is therefore the cost of accepting and evaluating an input thatmainrejects early.
CI reporting
The current CI workflow does not run benchmarks. For a lightweight first step, I would add a non-blocking benchmark job that compares base and head in the same runner, publishes the Markdown table through GITHUB_STEP_SUMMARY, and uploads the raw measurements as an artifact. I would not gate merges on wall-clock deltas from a shared GitHub-hosted runner for 3–120 ns operations.
For stable history, automatic base-branch comparison, and an updatable PR comment, CodSpeed's Divan integration is the cleaner option. A hand-written PR commenter needs pull-requests: write, while fork PR workflows normally receive a read-only token (GitHub documentation); a job summary avoids that permission/security complication.
Signed-off-by: tison <wander4096@gmail.com>
Summary
cargo x bench, preserving the Rust 1.85 MSRV.cargo x test,cargo x lint, the Rust 1.85 test and benchmark paths, and stable/nightlyno_stdcross-target builds.Closes #2.
Design Notes
The fractional conversion uses right-to-left base-10 scalar multiplication by the unit multiplier. After all fractional digits are consumed, the carry is the integral byte contribution and the final remainder digit determines the single round-half-up step. The largest supported multiplier is
2^60, so every intermediate remains withinu64and the implementation staysno_stdand allocation-free.On an Apple M4 Max, same-host Criterion comparisons against
mainshowed approximately 12% lower latency for plain integers, 25% for ordinary fractional units, and 21% for small fractional units. Groupedu64::MAXinputs remained effectively flat.