-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Explore adding an IVector<TSelf, T>
interface implemented by Vector128<T>
/Vector256<T>
#76244
Comments
Tagging subscribers to this area: @dotnet/area-system-numerics Issue DetailsIn many of our vectorized implementations, we now have a structure similar to the following: if (!Vector128.IsHardwareAccelerated || span.Length < Vector128<T>.Count)
{
... // scalar implementation
}
else if (!Vector256.IsHardwareAccelerated || span.Length < Vector256<T>.Count)
{
... // Vector128<T> implementation
}
else
{
... // Vector256<T> implementation
} In many cases, the public interface IVector<TSelf, T> { ... /* instance methods on both Vector128/256<T> and static methods from Vector128/256 */ }
public struct Vector128<T> : IVector<Vector128<T>, T> { ... }
public struct Vector256<T> : IVector<Vector256<T>, T> { ... } then we could likely collapse many of those two separate code paths into a single one, e.g. if (!Vector128.IsHardwareAccelerated || span.Length < Vector128<T>.Count)
{
... // scalar implementation
}
else if (!Vector256.IsHardwareAccelerated || span.Length < Vector256<T>.Count)
{
Process<Vector128<T>,T>(span);
}
else
{
Process<Vector128<T>,T>(span);
}
static void Process<TVector, T>(Span<T> span) where TVector : IVector<TVector, T>
{
... // single implementation in terms of TVector
} and save on some duplication. This could also potentially enable more advanced composition. For example, @adamsitnik was exploring the idea of an
|
Marking this as suggestion until I can get the actual proposal shape up. One interesting consideration is the This split namely impacts some APIs that are explicitly extension methods for perf reasons, but also is where we put APIs that are non-generic (such as To account for this, we'll need to determine if we want more APIs that are "nops", if exposing things like |
The second call to "Process" in your example should be with a Vector256, right? |
Yup, fixed, thanks. |
Created a very rough draft showing a proof of concept: #76423 |
Tagging subscribers to this area: @dotnet/area-system-runtime-intrinsics Issue DetailsIn many of our vectorized implementations, we now have a structure similar to the following: if (!Vector128.IsHardwareAccelerated || span.Length < Vector128<T>.Count)
{
... // scalar implementation
}
else if (!Vector256.IsHardwareAccelerated || span.Length < Vector256<T>.Count)
{
... // Vector128<T> implementation
}
else
{
... // Vector256<T> implementation
} In many cases, the public interface IVector<TSelf, T> { ... /* instance methods on both Vector128/256<T> and static methods from Vector128/256 */ }
public struct Vector128<T> : IVector<Vector128<T>, T> { ... }
public struct Vector256<T> : IVector<Vector256<T>, T> { ... } then we could likely collapse many of those two separate code paths into a single one, e.g. if (!Vector128.IsHardwareAccelerated || span.Length < Vector128<T>.Count)
{
... // scalar implementation
}
else if (!Vector256.IsHardwareAccelerated || span.Length < Vector256<T>.Count)
{
Process<Vector128<T>,T>(span);
}
else
{
Process<Vector256<T>,T>(span);
}
static void Process<TVector, T>(Span<T> span) where TVector : IVector<TVector, T>
{
... // single implementation in terms of TVector
} and save on some duplication. This could also potentially enable more advanced composition. For example, @adamsitnik was exploring the idea of an
|
In many of our vectorized implementations, we now have a structure similar to the following:
In many cases, the
Vector128<T>
andVector256<T>
implementations are identical other than "128" vs "256" in the type names used. If we had an interface that both types implemented:then we could likely collapse many of those two separate code paths into a single one, e.g.
and save on some duplication.
This could also potentially enable more advanced composition. For example, @adamsitnik was exploring the idea of an
IndexOfAny
method that would accept a struct to do the core processing, enabling IndexOfAny itself it implement all the boilerplate and then call to methods on that struct for the inner loop comparisons. That struct would implement an interface, and generic specialization would take care of ensuring everything could be inlined and efficient. But such a struct would need to be able to handle both Vector128 and Vector256 (and Vector512 presumably once it's in place), which would mean multiple methods on the interface that would all need to be implemented to do the same logic. If an IVector interface existed, such a struct could hopefully expose a single generic method constrained on IVector, and implementations would need to provide only one implementation, regardless of the vector width (assuming the implementation didn't require anything width-specific, of course).The text was updated successfully, but these errors were encountered: