Skip to content

Commit 72f9ee0

Browse files
Expose the Sin, Cos, and SinCos methods on the Vector types (#104848)
* Allow using a more efficient algorithm if twice the vector size is accelerated * Remove an unnecessary generic parameter from ExpDouble * Expose the Sin, Cos, and SinCos methods on the Vector types * Use the vector Sin, Cos, and SinCos methods where possible * Adding tests covering the vector Sin, Cos, and SinCos APIs * Fix some small bugs in the Sin, Cos, and SinCos impls * Ensure that very large inputs are handled * Ensure region is correctly adjusted when determining the sign of sin * Ensure that TernaryLogic lowering accounts for AND_NOT since it is not commutative * Don't vectorize too large SinPi or CosPi inputs for TensorPrimitives * Don't accelerate SinCosPi for the time being * Don't accelerate TensorPrimitives.SinCos for the time being * Don't include JIT changes, they were extracted to their own PR
1 parent 5fd965d commit 72f9ee0

File tree

29 files changed

+2932
-490
lines changed

29 files changed

+2932
-490
lines changed

src/libraries/Common/tests/System/GenericMathTestMemberData.cs

Lines changed: 245 additions & 1 deletion
Large diffs are not rendered by default.

src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Cos.cs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,24 @@ public static void Cos<T>(ReadOnlySpan<T> x, Span<T> destination)
6060
// 3. Reconstruction
6161
// Hence, cos(x) = sin(x + pi/2) = (-1)^N * sin(f)
6262

63-
public static bool Vectorizable => typeof(T) == typeof(float) || typeof(T) == typeof(double);
63+
public static bool Vectorizable => (typeof(T) == typeof(float))
64+
|| (typeof(T) == typeof(double));
6465

6566
public static T Invoke(T x) => T.Cos(x);
6667

6768
public static Vector128<T> Invoke(Vector128<T> x)
6869
{
70+
#if NET9_0_OR_GREATER
71+
if (typeof(T) == typeof(double))
72+
{
73+
return Vector128.Cos(x.AsDouble()).As<double, T>();
74+
}
75+
else
76+
{
77+
Debug.Assert(typeof(T) == typeof(float));
78+
return Vector128.Cos(x.AsSingle()).As<float, T>();
79+
}
80+
#else
6981
if (typeof(T) == typeof(float))
7082
{
7183
return CosOperatorSingle.Invoke(x.AsSingle()).As<float, T>();
@@ -75,10 +87,22 @@ public static Vector128<T> Invoke(Vector128<T> x)
7587
Debug.Assert(typeof(T) == typeof(double));
7688
return CosOperatorDouble.Invoke(x.AsDouble()).As<double, T>();
7789
}
90+
#endif
7891
}
7992

8093
public static Vector256<T> Invoke(Vector256<T> x)
8194
{
95+
#if NET9_0_OR_GREATER
96+
if (typeof(T) == typeof(double))
97+
{
98+
return Vector256.Cos(x.AsDouble()).As<double, T>();
99+
}
100+
else
101+
{
102+
Debug.Assert(typeof(T) == typeof(float));
103+
return Vector256.Cos(x.AsSingle()).As<float, T>();
104+
}
105+
#else
82106
if (typeof(T) == typeof(float))
83107
{
84108
return CosOperatorSingle.Invoke(x.AsSingle()).As<float, T>();
@@ -88,10 +112,22 @@ public static Vector256<T> Invoke(Vector256<T> x)
88112
Debug.Assert(typeof(T) == typeof(double));
89113
return CosOperatorDouble.Invoke(x.AsDouble()).As<double, T>();
90114
}
115+
#endif
91116
}
92117

93118
public static Vector512<T> Invoke(Vector512<T> x)
94119
{
120+
#if NET9_0_OR_GREATER
121+
if (typeof(T) == typeof(double))
122+
{
123+
return Vector512.Cos(x.AsDouble()).As<double, T>();
124+
}
125+
else
126+
{
127+
Debug.Assert(typeof(T) == typeof(float));
128+
return Vector512.Cos(x.AsSingle()).As<float, T>();
129+
}
130+
#else
95131
if (typeof(T) == typeof(float))
96132
{
97133
return CosOperatorSingle.Invoke(x.AsSingle()).As<float, T>();
@@ -101,9 +137,25 @@ public static Vector512<T> Invoke(Vector512<T> x)
101137
Debug.Assert(typeof(T) == typeof(double));
102138
return CosOperatorDouble.Invoke(x.AsDouble()).As<double, T>();
103139
}
140+
#endif
104141
}
105142
}
106143

144+
#if NET9_0_OR_GREATER
145+
// These are still used by CosPiOperator
146+
147+
private readonly struct CosOperatorSingle
148+
{
149+
internal const uint MaxVectorizedValue = 0x4A989680u;
150+
internal const uint SignMask = 0x7FFFFFFFu;
151+
}
152+
153+
private readonly struct CosOperatorDouble
154+
{
155+
internal const ulong SignMask = 0x7FFFFFFFFFFFFFFFul;
156+
internal const ulong MaxVectorizedValue = 0x4160000000000000ul;
157+
}
158+
#else
107159
/// <summary>float.Cos(x)</summary>
108160
private readonly struct CosOperatorSingle : IUnaryOperator<float, float>
109161
{
@@ -347,5 +399,6 @@ public static Vector512<double> Invoke(Vector512<double> x)
347399
return (poly.AsUInt64() ^ odd).AsDouble();
348400
}
349401
}
402+
#endif
350403
}
351404
}

src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.CosPi.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ public static void CosPi<T>(ReadOnlySpan<T> x, Span<T> destination)
3333
private readonly struct CosPiOperator<T> : IUnaryOperator<T, T>
3434
where T : ITrigonometricFunctions<T>
3535
{
36-
public static bool Vectorizable => typeof(T) == typeof(float) || typeof(T) == typeof(double);
36+
public static bool Vectorizable => (typeof(T) == typeof(float))
37+
|| (typeof(T) == typeof(double));
3738

3839
public static T Invoke(T x) => T.CosPi(x);
3940

4041
public static Vector128<T> Invoke(Vector128<T> x)
4142
{
4243
Vector128<T> xpi = x * Vector128.Create(T.Pi);
44+
4345
if (typeof(T) == typeof(float))
4446
{
4547
if (Vector128.GreaterThanAny(xpi.AsUInt32() & Vector128.Create(CosOperatorSingle.SignMask), Vector128.Create(CosOperatorSingle.MaxVectorizedValue)))
@@ -62,6 +64,7 @@ public static Vector128<T> Invoke(Vector128<T> x)
6264
public static Vector256<T> Invoke(Vector256<T> x)
6365
{
6466
Vector256<T> xpi = x * Vector256.Create(T.Pi);
67+
6568
if (typeof(T) == typeof(float))
6669
{
6770
if (Vector256.GreaterThanAny(xpi.AsUInt32() & Vector256.Create(CosOperatorSingle.SignMask), Vector256.Create(CosOperatorSingle.MaxVectorizedValue)))
@@ -84,6 +87,7 @@ public static Vector256<T> Invoke(Vector256<T> x)
8487
public static Vector512<T> Invoke(Vector512<T> x)
8588
{
8689
Vector512<T> xpi = x * Vector512.Create(T.Pi);
90+
8791
if (typeof(T) == typeof(float))
8892
{
8993
if (Vector512.GreaterThanAny(xpi.AsUInt32() & Vector512.Create(CosOperatorSingle.SignMask), Vector512.Create(CosOperatorSingle.MaxVectorizedValue)))

src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.FloatHelpers.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,79 @@ private static Vector256<double> ApplyScalar<TOperator>(Vector256<double> double
2424

2525
private static Vector512<double> ApplyScalar<TOperator>(Vector512<double> doubles) where TOperator : IUnaryOperator<double, double> =>
2626
Vector512.Create(ApplyScalar<TOperator>(doubles.GetLower()), ApplyScalar<TOperator>(doubles.GetUpper()));
27+
28+
private static (Vector128<float> First, Vector128<float> Second) Apply2xScalar<TOperator>(Vector128<float> floats)
29+
where TOperator : IUnaryInputBinaryOutput<float>
30+
{
31+
(float firstRes0, float secondRes0) = TOperator.Invoke(floats[0]);
32+
(float firstRes1, float secondRes1) = TOperator.Invoke(floats[1]);
33+
(float firstRes2, float secondRes2) = TOperator.Invoke(floats[2]);
34+
(float firstRes3, float secondRes3) = TOperator.Invoke(floats[3]);
35+
36+
return (
37+
Vector128.Create(firstRes0, firstRes1, firstRes2, firstRes3),
38+
Vector128.Create(secondRes0, secondRes1, secondRes2, secondRes3)
39+
);
40+
}
41+
42+
private static (Vector256<float> First, Vector256<float> Second) Apply2xScalar<TOperator>(Vector256<float> floats)
43+
where TOperator : IUnaryInputBinaryOutput<float>
44+
{
45+
(Vector128<float> firstLower, Vector128<float> secondLower) = Apply2xScalar<TOperator>(floats.GetLower());
46+
(Vector128<float> firstUpper, Vector128<float> secondUpper) = Apply2xScalar<TOperator>(floats.GetUpper());
47+
48+
return (
49+
Vector256.Create(firstLower, firstUpper),
50+
Vector256.Create(secondLower, secondUpper)
51+
);
52+
}
53+
54+
private static (Vector512<float> First, Vector512<float> Second) Apply2xScalar<TOperator>(Vector512<float> floats)
55+
where TOperator : IUnaryInputBinaryOutput<float>
56+
{
57+
(Vector256<float> firstLower, Vector256<float> secondLower) = Apply2xScalar<TOperator>(floats.GetLower());
58+
(Vector256<float> firstUpper, Vector256<float> secondUpper) = Apply2xScalar<TOperator>(floats.GetUpper());
59+
60+
return (
61+
Vector512.Create(firstLower, firstUpper),
62+
Vector512.Create(secondLower, secondUpper)
63+
);
64+
}
65+
66+
private static (Vector128<double> First, Vector128<double> Second) Apply2xScalar<TOperator>(Vector128<double> doubles)
67+
where TOperator : IUnaryInputBinaryOutput<double>
68+
{
69+
(double firstRes0, double secondRes0) = TOperator.Invoke(doubles[0]);
70+
(double firstRes1, double secondRes1) = TOperator.Invoke(doubles[1]);
71+
72+
return (
73+
Vector128.Create(firstRes0, firstRes1),
74+
Vector128.Create(secondRes0, secondRes1)
75+
);
76+
}
77+
78+
private static (Vector256<double> First, Vector256<double> Second) Apply2xScalar<TOperator>(Vector256<double> doubles)
79+
where TOperator : IUnaryInputBinaryOutput<double>
80+
{
81+
(Vector128<double> firstLower, Vector128<double> secondLower) = Apply2xScalar<TOperator>(doubles.GetLower());
82+
(Vector128<double> firstUpper, Vector128<double> secondUpper) = Apply2xScalar<TOperator>(doubles.GetUpper());
83+
84+
return (
85+
Vector256.Create(firstLower, firstUpper),
86+
Vector256.Create(secondLower, secondUpper)
87+
);
88+
}
89+
90+
private static (Vector512<double> First, Vector512<double> Second) Apply2xScalar<TOperator>(Vector512<double> doubles)
91+
where TOperator : IUnaryInputBinaryOutput<double>
92+
{
93+
(Vector256<double> firstLower, Vector256<double> secondLower) = Apply2xScalar<TOperator>(doubles.GetLower());
94+
(Vector256<double> firstUpper, Vector256<double> secondUpper) = Apply2xScalar<TOperator>(doubles.GetUpper());
95+
96+
return (
97+
Vector512.Create(firstLower, firstUpper),
98+
Vector512.Create(secondLower, secondUpper)
99+
);
100+
}
27101
}
28102
}

src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Sin.cs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,24 @@ public static void Sin<T>(ReadOnlySpan<T> x, Span<T> destination)
5050
//
5151
// The term sin(f) can be approximated by using a polynomial
5252

53-
public static bool Vectorizable => typeof(T) == typeof(float) || typeof(T) == typeof(double);
53+
public static bool Vectorizable => (typeof(T) == typeof(float))
54+
|| (typeof(T) == typeof(double));
5455

5556
public static T Invoke(T x) => T.Sin(x);
5657

5758
public static Vector128<T> Invoke(Vector128<T> x)
5859
{
60+
#if NET9_0_OR_GREATER
61+
if (typeof(T) == typeof(double))
62+
{
63+
return Vector128.Sin(x.AsDouble()).As<double, T>();
64+
}
65+
else
66+
{
67+
Debug.Assert(typeof(T) == typeof(float));
68+
return Vector128.Sin(x.AsSingle()).As<float, T>();
69+
}
70+
#else
5971
if (typeof(T) == typeof(float))
6072
{
6173
return SinOperatorSingle.Invoke(x.AsSingle()).As<float, T>();
@@ -65,10 +77,22 @@ public static Vector128<T> Invoke(Vector128<T> x)
6577
Debug.Assert(typeof(T) == typeof(double));
6678
return SinOperatorDouble.Invoke(x.AsDouble()).As<double, T>();
6779
}
80+
#endif
6881
}
6982

7083
public static Vector256<T> Invoke(Vector256<T> x)
7184
{
85+
#if NET9_0_OR_GREATER
86+
if (typeof(T) == typeof(double))
87+
{
88+
return Vector256.Sin(x.AsDouble()).As<double, T>();
89+
}
90+
else
91+
{
92+
Debug.Assert(typeof(T) == typeof(float));
93+
return Vector256.Sin(x.AsSingle()).As<float, T>();
94+
}
95+
#else
7296
if (typeof(T) == typeof(float))
7397
{
7498
return SinOperatorSingle.Invoke(x.AsSingle()).As<float, T>();
@@ -78,10 +102,22 @@ public static Vector256<T> Invoke(Vector256<T> x)
78102
Debug.Assert(typeof(T) == typeof(double));
79103
return SinOperatorDouble.Invoke(x.AsDouble()).As<double, T>();
80104
}
105+
#endif
81106
}
82107

83108
public static Vector512<T> Invoke(Vector512<T> x)
84109
{
110+
#if NET9_0_OR_GREATER
111+
if (typeof(T) == typeof(double))
112+
{
113+
return Vector512.Sin(x.AsDouble()).As<double, T>();
114+
}
115+
else
116+
{
117+
Debug.Assert(typeof(T) == typeof(float));
118+
return Vector512.Sin(x.AsSingle()).As<float, T>();
119+
}
120+
#else
85121
if (typeof(T) == typeof(float))
86122
{
87123
return SinOperatorSingle.Invoke(x.AsSingle()).As<float, T>();
@@ -91,9 +127,25 @@ public static Vector512<T> Invoke(Vector512<T> x)
91127
Debug.Assert(typeof(T) == typeof(double));
92128
return SinOperatorDouble.Invoke(x.AsDouble()).As<double, T>();
93129
}
130+
#endif
94131
}
95132
}
96133

134+
#if NET9_0_OR_GREATER
135+
// These are still used by SinPiOperator
136+
137+
private readonly struct SinOperatorSingle
138+
{
139+
internal const uint MaxVectorizedValue = 0x49800000u;
140+
internal const uint SignMask = 0x7FFFFFFFu;
141+
}
142+
143+
private readonly struct SinOperatorDouble
144+
{
145+
internal const ulong SignMask = 0x7FFFFFFFFFFFFFFFul;
146+
internal const ulong MaxVectorizedValue = 0x4160000000000000ul;
147+
}
148+
#else
97149
/// <summary>float.Sin(x)</summary>
98150
private readonly struct SinOperatorSingle : IUnaryOperator<float, float>
99151
{
@@ -334,5 +386,6 @@ public static Vector512<double> Invoke(Vector512<double> x)
334386
return (poly.AsUInt64() ^ (x.AsUInt64() & Vector512.Create(~SignMask)) ^ odd).AsDouble();
335387
}
336388
}
389+
#endif
337390
}
338391
}

src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.SinPi.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ public static void SinPi<T>(ReadOnlySpan<T> x, Span<T> destination)
3333
private readonly struct SinPiOperator<T> : IUnaryOperator<T, T>
3434
where T : ITrigonometricFunctions<T>
3535
{
36-
public static bool Vectorizable => typeof(T) == typeof(float) || typeof(T) == typeof(double);
36+
public static bool Vectorizable => (typeof(T) == typeof(float))
37+
|| (typeof(T) == typeof(double));
3738

3839
public static T Invoke(T x) => T.SinPi(x);
3940

4041
public static Vector128<T> Invoke(Vector128<T> x)
4142
{
4243
Vector128<T> xpi = x * Vector128.Create(T.Pi);
44+
4345
if (typeof(T) == typeof(float))
4446
{
4547
if (Vector128.GreaterThanAny(xpi.AsUInt32() & Vector128.Create(SinOperatorSingle.SignMask), Vector128.Create(SinOperatorSingle.MaxVectorizedValue)))
@@ -62,6 +64,7 @@ public static Vector128<T> Invoke(Vector128<T> x)
6264
public static Vector256<T> Invoke(Vector256<T> x)
6365
{
6466
Vector256<T> xpi = x * Vector256.Create(T.Pi);
67+
6568
if (typeof(T) == typeof(float))
6669
{
6770
if (Vector256.GreaterThanAny(xpi.AsUInt32() & Vector256.Create(SinOperatorSingle.SignMask), Vector256.Create(SinOperatorSingle.MaxVectorizedValue)))
@@ -84,6 +87,7 @@ public static Vector256<T> Invoke(Vector256<T> x)
8487
public static Vector512<T> Invoke(Vector512<T> x)
8588
{
8689
Vector512<T> xpi = x * Vector512.Create(T.Pi);
90+
8791
if (typeof(T) == typeof(float))
8892
{
8993
if (Vector512.GreaterThanAny(xpi.AsUInt32() & Vector512.Create(SinOperatorSingle.SignMask), Vector512.Create(SinOperatorSingle.MaxVectorizedValue)))

0 commit comments

Comments
 (0)