Skip to content

Avoid native fmod in JsNumber.Create(double) integral detection#2662

Merged
lahma merged 1 commit into
sebastienros:mainfrom
lahma:perf-create-double-no-fmod
Jul 13, 2026
Merged

Avoid native fmod in JsNumber.Create(double) integral detection#2662
lahma merged 1 commit into
sebastienros:mainfrom
lahma:perf-create-double-no-fmod

Conversation

@lahma

@lahma lahma commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

JsNumber.Create(double) calls TypeConverter.IsIntegralNumber, whose value % 1 == 0 compiles to a native fmod call — paid on every double boxing, integral or fractional. ETW sampling (ultra @ 8190 Hz) of SunSpider math scripts shows ucrtbase!fmod at several percent of main-thread self time in workloads that contain no % operator at all.

Replace the check inside Create(double) with a (long) round-trip equality: NaN and ±Infinity saturate on the cast and fail the equality, fractional values fail it, and the explicit > long.MinValue and < long.MaxValue range comparisons are kept so the two boundary doubles behave exactly as before. TypeConverter.IsIntegralNumber itself is unchanged — spec consumers (e.g. Number.isInteger) must classify out-of-long-range values like 1e300 as integral, which the round-trip intentionally does not.

Benchmarks (default jobs, vs same-day baseline on same machine)

Benchmark main PR Δ
FunctionLocalNumberLoop AccumulatorWithCallArg 23.98 ms 21.63 ms −9.8%
FunctionLocalNumberLoop MixedArithmetic 10.85 ms 10.28 ms −5.3%
SunSpider math-partial-sums 53.72 ms 52.25 ms −2.7%
SunSpider math-spectral-norm 53.84 ms 54.22 ms neutral (drift band)
SunSpider math-cordic 114.91–120.44 ms (drift) 116.06 ms neutral
FunctionLocalNumberLoop DoubleAccumulator / LargeIntCounter 2.24 / 2.21 ms (re-run) 2.23 / 2.25 ms neutral

ETW verification on a spectral-norm load-loop driver: fmod disappears from the profile; Native-category self time 540 → 346 ms for identical work.

Gates

  • Jint.Tests: 3537 (net10.0) + 3474 (net472) passed, 0 failed
  • Jint.Tests.PublicInterface: 82 × 2 TFMs passed
  • Jint.Tests.CommonScripts: 28 × 2 TFMs passed
  • Test262: 99,429 passed, 0 failed (Number boundary tests exercise the long.MinValue/MaxValue edges)

🤖 Generated with Claude Code

TypeConverter.IsIntegralNumber uses `value % 1 == 0`, which compiles to a
native fmod call - paid on every Create(double) on arithmetic-heavy
workloads (ETW: fmod visible at several percent of self time on SunSpider
math scripts). A (long) round-trip equality detects the same integral
values: NaN and infinities saturate on the cast and fail the equality,
and the explicit range comparisons keep the exact long.MinValue/MaxValue
boundary behavior of the previous check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant