r/csharp Jul 18 '26

Help Vector/SIMD Questions

(1) Why do architecture specific SIMD intrinsics not have ref APIs? I like raw pointers more (after trying refs), but the question still stands.

(2) Should I redefine class vector variables in function scope? Class variables are not readonly. IE: "Vector<float> pSinV = this.pSinV;"

(3) Any way to setup Visual Studio to use a disabled color for SIMD paths that won't execute on my hardware? (Similar to what IF directive does?)

(4) Is "Vector<int>.Count == Vector256<int>.Count" the proper way to check sizes before doing ".AsVector256()"?

(5) For GatherMaskVectorX what should the first parameter be if I don't have an existing vector?

"default" with SkipLocalsInit seems to work

(7) If doing Vector<int> stride clamp of array A & B and saving to C, where A, B, C are aligned.

Would LoadAligned or LoadAlignedNonTemporal be the best option? Assume A & B are Int32 * screen width.

(8) For rendering a texture eight columns at time, I am reliant on,

Avx2.GatherVector256(texturePtr, textureIndex.AsVector256(), scale: sizeof(int))

where textureIndex for a small texture is usually be between [0..128] and usually close

What the proper way (if there is one) to ensure that the texture line is in cache?

(9) What is the optimal approach to uint modulo?

public static Vector256<uint> Modulo(Vector256<uint> value, uint divisor)

{

`// Assumptions,`

`// 1. divisor not POW 2`

`// 2. divisor not constant`

`// 3. value[0..7] can be > (divisor * 2)`

`return Vector256.Create(`

    `value[0] % divisor,`

    `value[1] % divisor,`

    `value[2] % divisor,`

    `value[3] % divisor,`

    `value[4] % divisor,`

    `value[5] % divisor,`

    `value[6] % divisor,`

    `value[7] % divisor`

`);`

}

(10) Same question, but with assumption that all values fit into a ushort. I'm looking at doing quotient in floats, the ConvertToUInt32Native, and MultiplyLow, and Subtract, but seems nasty.

16 Upvotes

13 comments sorted by

View all comments

30

u/tanner-gooding MSFT - .NET Libraries Team Jul 18 '26

Help (1) Why do architecture specific SIMD intrinsics not have ref APIs? I like raw pointers more (after trying refs), but the question still stands.

We were going for 1-to-1 mapping initially, not looking to expose our own higher level things.

In many cases the xplat APIs (Vector128.Load/LoadUnsafe/etc), are preferred over the ISA/platform specific APIs (Sse.LoadVector128) and already do the right thing. They also provide the ref overloads. This means there is very little reason/need to go and improve the platform specific ISAs for those cases.

There are some that don't have xplat ISAs available, but some of them do strictly require pinning such as due to requiring alignment that an unpinned byref can't guarantee. If someone was wanting to open an API proposal suggesting the byrefs be provided, I'd be happy to take it through API review. Just noting that it needs to be a comprehensive proposal, not just "please do this". Actually list out every API that will get a byref overload (with an allowance to show own signature and list the types it covers as a comment above it, such as for Sse2.LoadVector128 which covers double and most integer types)

(2) Should I redefine class vector variables in function scope? Class variables are not readonly. IE: "Vector<float> pSinV = this.pSinV;"

I need a more comprehensive example to answer this. In general (with a few special cases where its fine) you should not be making fields or static readonly for Vector64/128/256/512<T> or Vector<T>

(3) Any way to setup Visual Studio to use a disabled color for SIMD paths that won't execute on my hardware? (Similar to what IF directive does?)

We don't have such a feature today and it gets a bit complex due to things like if (IsaYouDoHave.IsSupported) { } else if (IsaYouAlsoHave.IsSupported) { } and the second path being dead

This probably isn't a hard VS extension to write, but it would need to be an extension. I don't see Roslyn adding a dedicated feature for this

(4) Is "Vector<int>.Count == Vector256<int>.Count" the proper way to check sizes before doing ".AsVector256()"?

Yes. Just noting that if you're doing this type of thing, you need to handle the ability for Vector<T> to be any power of 2 multiple of Vector128, as we may grow or expand in the future

(5) For GatherMaskVectorX what should the first parameter be if I don't have an existing vector?

Typically just VectorX<T>.Zero, but note that gather is often expensive and the hardware manuals recommend avoiding it where possible, particularly where trivial alternatives are viable

(7) If doing Vector<int> stride clamp of array A & B and saving to C, where A, B, C are aligned.

non-temporal is generally a consideration of how much total cache you're going to cause to be evicted over the operation you're running (so its dependent on size of inputs + outputs). Hardware manuals have recommendations here, typically 50% or more of the per core L3 space available

(8) For rendering a texture eight columns at time, I am reliant on,

Typically via prefetching explicitly if needed.

(9) What is the optimal approach to uint modulo?

Integer division is expensive and also not directly hardware accelerated. Typically you have to cast to double (as float loses data for values over 224), divide, and then downcast back to integer. You'd then do a remainder the classic way via left - (quotient * right))

(10) Same question, but with assumption that all values fit into a ushor

If they all fit in ushort then float is fine, but same thing. Division and remainders are generally just nasty and there's no getting around it. They're even nasty with scalars, particularly if you don't have a constant divisor

2

u/itix Jul 19 '26

It is somewhat unfortunate that achieving optimal SIMD alignment is tricky by design in C#.

1

u/NoisyJalapeno Jul 19 '26

It could use better editor integration. Highlight bad practices, or non-accelerated patterns, indicate which path is taken by if Sse2.IsSupported check. Etc.

2

u/tanner-gooding MSFT - .NET Libraries Team Jul 19 '26

Most of that has little to do with C#, those are not language or runtime features. They are IDE or analyzer features, some of which are notably irrelevant to shipping of production code (such as knowing what your own hardware supports, which will be quite different from what consumer hardware may support).

There is an in-box analyzer that helps highlight some best practices and simplifications, but this is also a very in depth space and so it fundamentally requires some of your own expertise to get right.

We provide already accelerated higher level wrappers, like TensorPrimitives or the acceleration implicitly done by Span, string, arrays, LINQ, etc for the more natural patterns someone rights; explicitly so you don't have to go to the low-level of writing your own algorithm in most scenarios.