stride icon indicating copy to clipboard operation
stride copied to clipboard

Add common type conversions

Open Doprez opened this issue 1 year ago • 4 comments

PR Details

This PR adds some useful extensions that are commonly used with external libraries since Numerics has become the standard.

Related Issue

https://github.com/stride3d/stride/pull/2131 this is a great example where something like this could be used.

Motivation and Context

the Bepu branch has this built into the library but its also used in the model importer as well as any library that may need these fast conversions.

Types of changes

  • [ ] Docs change / refactoring / dependency upgrade
  • [ ] Bug fix (non-breaking change which fixes an issue)
  • [x] New feature (non-breaking change which adds functionality)
  • [ ] Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • [ ] My change requires a change to the documentation.
  • [ ] I have added tests to cover my changes.
  • [ ] All new and existing tests passed.
  • [ ] I have built and run the editor to try this change out.

Doprez avatar Apr 27 '24 17:04 Doprez

I think if you pass by ref (use in and Unsafe.AsRef) and apply [MethodImpl(MethodImplOptions.AggressiveInlining)], the conversions should be basically free.

timcassell avatar May 13 '24 20:05 timcassell

I did some quick tests with Inlining and the performance was the same or similar on a few runs. It seems like the compiler does not make any significant changes with or witthout it to the compiled code, I tested with the Bepu library mentioned above.

Unless Im missing something I cant seem to use Unsafe.AsRef for conversion to another type?

Doprez avatar May 14 '24 00:05 Doprez

Here's a couple of variants with the jitted asm shown: https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBDAzgWwB8ABABgAIA5ANRjA2gCZyBec4gRgDpKBXfGFACWYXF1r0mAbgCwAKDLtuAJV4A7DEIFcAwhHwAHIQBtBAZUEA3ETFyy584gGYlSdsx3kA3vPJ/yvv7O5LgYULz05BIMUIyBfj5y/snsLgBmxhDYGOQIaOQAnvbJAL7xAUlBLpxu0UzkAEJ4MAAUGAAWQrhUdbHklgCUrAB85ACqarjYaTBcAIK4ADw0dDGM+b2Mwy2waf0Dxf7lwTXk6lMzUav1AAphOngYbZ3dK5J9gyPkAFQtm98DABklkOfmO1Q4tWufWUMDSzy65F2PWhzE+LFGEwuswWy02G1R22Rg1BFWSAG0ALIwDoQAAmAElDMYWtTaYzmQB5AyaCCTeYAcwFsFwuCElhgDLUxiEallAoGAF1wa4ru9mLC0lKZWoYHSEd1kW81vsvljpjilsamAT1US4ftSSrTptGkIMGBHgaUerTRjxpMLVwGu6HqE8ajbWttiTys7IWqTSGPY8pS1ZeQOojrR8hv7zTNg6HHhH1VGmDGDnHKmCa6lVa6ACq2DBNXCtHNogblRIpfyuqCsfrB5otKt15INWDYADWYwMjfaME5N1wY9JyWIAHYkaSynX41Dfc3QncoGGnp39j3yskB0PLFwzxf17f/FOYLP54vl6vXxOgh3KA92rTcISPE0TwwTUWivQYbwAvx7zYR8YPHPs/A/L8FyXFc13QjDt13cp92SQ9E3qKDNW1WU9Vg114LrXsMOQ4dqOlWj9QIvssLnHDf3wjdAOIutSKOOsTgTJsW2TT1Qno1FryYt8kMUwcUKLFN5O4lJeO/XC/x0zcgJAg8JPAii+ig2TUzUBTfUY5JmL7VjHxs0I0yM99pz4n88P/DD2BMkjQKCBNiBQRofP0gSxwQwLOC4TgAE4WgAIgAEX4fACnIAR2UzCByBgSZeFgcg8HwchEVwGUBXaDBjFy/BoBgJEYAMEUSowbJxTaiA9mwdrsGMcg0nUeghD5NKdP3EogA==

As you can see, the jit tends to inline such a small function regardless of the attribute. The newly introduced BitCast seems like the best of both world here as the input doesn't need to be passed as ref/in to have improved assembly (see asm for C.TestBitcast).

Eideren avatar May 14 '24 00:05 Eideren

Ah nice, BitCast looks awesome. I was imagining it like this, which it looks like BitCast creates the same code in a cleaner way.

public static Vector2 ToStride(this in NVector2 v) => Unsafe.As<NVector2, Vector2>(ref Unsafe.AsRef(v));

The in modifier is to prevent pass-by-value copies (which you can see in the asm of TestBase vs TestRef).

as the input doesn't need to be passed as ref/in to have improved assembly (see asm for C.TestBitcast).

The assembly for Bitcast vs BitcastIn is different though. It looks like it's simply better with in (in case the JIT doesn't actually inline it, which is not guaranteed).

C.Bitcast(System.Numerics.Vector2)
    L0000: vzeroupper
    L0003: vmovq xmm0, rcx
    L0008: vmovq rax, xmm0
    L000d: ret

C.BitcastIn(System.Numerics.Vector2 ByRef)
    L0000: mov rax, [rcx]
    L0003: ret

timcassell avatar May 14 '24 00:05 timcassell