Skip to content

Consistent results in calculations #152

Closed

Description

@mmozeiko

Math library like this should produce consistent results regardless where it runs. Currently this won't happen because of use of _mm_rsqrt_ss intrinsic and other places. This instruction produces different results on different systems because of how it allows different precision. Lower precision is fine in general, as long as it documented, but inconsistent results is not. rsqrt is well known to have different results on Intel vs AMD cpu's. This can lead to quite bad situations:

  1. clients having bugs in physics, lighting or any other math calculations. But you won't be able to reproduce issues if running different CPU. It's especially critical for physics simulations where determinism is must have.

  2. bad shared cache / CI system trashing. Let's say you have asset cache, where you process asset (packaged mesh, compressed texture, etc..), produce hash of it, and if hash is not present in cache you upload asset. But because of this inconsistency if other user or CI system runs on different cpu - asset hash will be different, so you will be uploading asset even if nothing changed. And other people who just wants to get latest data from cache (typically artists who don't touch game code) will need downloading newer asset data when nothing changed. Just useless waste of time of uploading & downloading assets to/from cache.

  3. insane patch sizes. Let's say you changed one texture, or maybe fixed one mesh. You rebuild game asset files. Run diff for previous version to create patch file, and expect change in few KB's or few MB's because of tiny change. But because you run it on different system this might change every single texture or mesh or wherever math is used. And you get 20GB patch file (just for tiny change)!

Again - reduced precision is OK. Inconsistent precision is NOT.

Here is list that I found that does not provide consistency:

  1. InvSqrtF because of _mm_rsqrt_ss - Intel vs AMD differences. Or when compiled for non-x86 arch.
    rsqrt made sense 15 or something years back when CPU's were slower. Now they are fast. Just do proper sqrt instruction. It will be fine. And in cases it will become performance, usually you want to optimize code explicitly anyway instead of relying on generic math library.

  2. DotV4, DotQ, MulQ - they will produce different results for way you written math in SSE vs non-SSE version. Float addition is not associative.

Activity

  1. RandyGaul commented on Jan 23, 2023

    @RandyGaul

    Just chiming in to echo Martin's take here. I know this is an old topic and I've already said my 2 cents, but want to reiterate that the precision vs perf tradeoff here, even without considering platform dependent results, is no longer a good tradeoff considering how quick modern hardware does a real sqrt operation.

  2. bvisness commented on Jan 23, 2023

    @bvisness
    Member

    Makes sense. To be honest I'm debating the merits of our hand-rolled SIMD code across the board. Certainly it doesn't seem worthwhile for small things like inverse square root.

    I'll rework things to avoid specialized inverse square roots.

  3. strangezakary commented on Jan 23, 2023

    @strangezakary
    Member

    Yep agreed. We should finally deal with this now with 2.0.

  4. bvisness commented on Jan 23, 2023

    @bvisness
    Member

    Well to be clear, I'm suggesting we maybe just delete all our SIMD code. I'm not persuaded it's worth it. I mean maybe we could keep it for mat4 multiplies or something, but in general, I really doubt it's worth it. But SIMD is not my forte, so I'd defer to @mmozeiko on this one.

  5. mmozeiko commented on Jan 23, 2023

    @mmozeiko
    Author

    It's fine to leave SIMD code if you want it. Just make sure scalar path matches SIMD float calculations. Doing SIMD on SSE & NEON will not break it, they produce same results. So SIMD by itself is not a problem.

    Like for dot product that would be writing it as: return (x + z) + (y + w);

  6. RandyGaul commented on Jan 23, 2023

    @RandyGaul

    Having a scalar mode is good for e.g. emscripten builds, but in general SIMD is a nice thing to keep. You don't need to SIMD everything though, and if the maintenance cost is high you can definitely simplify and have less SIMD implementation.

  7. Jack-Punter commented on Jan 23, 2023

    @Jack-Punter
    Contributor

    You don't need to SIMD everything though, and if the maintenance cost is high you can definitely simplify and have less SIMD implementation.

    I was looking at adding NOEN on a local branch yesterday, and was running some (very rudimentary, and not exhaustive) benchmarks. and it looks like SIMD is not worth it for Vec4 unless you care about debug performance, optimized builds at -O2 perform the same scalar vs SIMD. You do see an improvement for matrix transpose, need to add some benchmarks for other matrix ops, and Quaternions to see what else sees benefit. The same is true with both NEON and SSE, so If you wanted to remove some of the SIMD paths to simplify, its probably pretty safe to just use SIMD for matrices.

  8. bvisness commented on Feb 20, 2023

    @bvisness
    Member

    I believe we should have consistent results between SIMD and non-SIMD code now. If you find any other inconsistencies, please let me know and I will reopen the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions