Open Bug 1937103 Opened 2 months ago Updated 2 months ago

Math.hypot() is 9x slower than Math.sqrt() (We are overall faster than Chrome)

Categories

(Core :: JavaScript Engine, defect, P3)

Firefox 133
defect

Tracking

()

People

(Reporter: ashley, Unassigned)

References

(Blocks 1 open bug)

Details

Attachments

(1 file)

User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0

Steps to reproduce:

Math.hypot() appears to be significantly slower than Math.sqrt(). As it is a more convenient specialized method for calculating the hypotenuse, this makes it easy to write code that is accidentally less performant than expected. This affects our browser-based game engine Construct (https://www.construct.net) as well as third-party math libraries like glMatrix (who eventually realized the same thing and removed uses of Math.hypot(): https://github.com/toji/gl-matrix/commit/8962b2e7727594022e59e48e605049c69403da60)

See attachment for what I believe is a fair benchmark.

Actual results:

On my machine after three runs, the Math.sqrt() benchmark completes in ~34ms, and the Math.hypot() benchmark completes in ~201ms (~6x slower).

Expected results:

Both benchmarks should complete in a similar time. If anything perhaps Math.hypot() should be faster, as more of the calculation is completed with a built-in method.

Note that optimizing the case where 2 or 3 arguments are passed should be sufficient to optimize the majority of performance-sensitive uses in game engines using 2D and 3D calculations.

Component: Untriaged → JavaScript Engine
Product: Firefox → Core

Nightly:

Sqrt : 57msm 54ms, 59ms
hypot: 415ms, 411ms,414ms
Profile: https://share.firefox.dev/41xx0Ga (1.5 seconds)

Chrome:

Sqrt : 245ms, 247ms, 249ms
hypot: 491ms, 495ms,477ms
Profile: https://share.firefox.dev/3ZTAamH (2.4 seconds)

Summary: Math.hypot() is significantly slower than Math.sqrt() → Math.hypot() is 9x slower than Math.sqrt() (We are overall faster than Chrome)

I think we're just faster than Chrome, because our Math.random is faster.

For Math.sqrt we use the sqrt CPU instruction directly.

We specialize Math.hypot with 2-4 arguments, but we currently always do a call to C++ code. For two arguments we call ecmaHypot which calls fdlibm_hypot, and we have a different implementation if there are more than two arguments.

To fix this we could change ecmaHypot to use hypot_step too. It should then be possible to inline the same code in the JITs, but it's a lot more machine code than add + sqrt so it'll likely still be slower.

Replacing the fdlibm_hypot call with the equivalent of hypot4 but for two arguments is slower for me in the JS shell, but that seems to be mainly because Clang didn't inline the std::isinf/std::isnan calls.

Blocks: sm-js-perf
Severity: -- → S3
Priority: -- → P3
Status: UNCONFIRMED → NEW
Ever confirmed: true
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: