Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
aalmada committed Jun 13, 2023
1 parent f224d99 commit 31c43cc
Show file tree
Hide file tree
Showing 21 changed files with 189 additions and 149 deletions.
12 changes: 6 additions & 6 deletions NetFabric.Numerics.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetFabric.Numerics", "src\N
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetFabric.Numerics.Angle", "src\NetFabric.Numerics.Angle\NetFabric.Numerics.Angle.csproj", "{6A408F78-4EE3-4F3D-8E88-D18096386544}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetFabric.Numerics.Angle.Benchmarks", "src\NetFabric.Numerics.Angle.Benchmarks\NetFabric.Numerics.Angle.Benchmarks.csproj", "{6770B944-2904-4484-A854-A813CB1BF2EB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetFabric.Numerics.Angle.UnitTests", "src\NetFabric.Numerics.Angle.UnitTests\NetFabric.Numerics.Angle.UnitTests.csproj", "{D3F1C490-2386-4BBC-8559-35AC6F53D2F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetFabric.Numerics.Geography", "src\NetFabric.Numerics.Geography\NetFabric.Numerics.Geography.csproj", "{EF5F4630-2DBC-42F3-A4CA-15DEF059BE84}"
Expand Down Expand Up @@ -55,6 +53,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Api", "Api", "{83BDB68A-4F4
docs\api\index.md = docs\api\index.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetFabric.Numerics.Benchmarks", "src\NetFabric.Numerics.Benchmarks\NetFabric.Numerics.Benchmarks.csproj", "{22D5FBE1-04E3-4B84-94A5-C2B959A51D68}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -69,10 +69,6 @@ Global
{6A408F78-4EE3-4F3D-8E88-D18096386544}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6A408F78-4EE3-4F3D-8E88-D18096386544}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6A408F78-4EE3-4F3D-8E88-D18096386544}.Release|Any CPU.Build.0 = Release|Any CPU
{6770B944-2904-4484-A854-A813CB1BF2EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6770B944-2904-4484-A854-A813CB1BF2EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6770B944-2904-4484-A854-A813CB1BF2EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6770B944-2904-4484-A854-A813CB1BF2EB}.Release|Any CPU.Build.0 = Release|Any CPU
{D3F1C490-2386-4BBC-8559-35AC6F53D2F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D3F1C490-2386-4BBC-8559-35AC6F53D2F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D3F1C490-2386-4BBC-8559-35AC6F53D2F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -89,6 +85,10 @@ Global
{F269C2EB-68AF-490B-B728-7E4EDE43F1B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F269C2EB-68AF-490B-B728-7E4EDE43F1B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F269C2EB-68AF-490B-B728-7E4EDE43F1B0}.Release|Any CPU.Build.0 = Release|Any CPU
{22D5FBE1-04E3-4B84-94A5-C2B959A51D68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22D5FBE1-04E3-4B84-94A5-C2B959A51D68}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22D5FBE1-04E3-4B84-94A5-C2B959A51D68}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22D5FBE1-04E3-4B84-94A5-C2B959A51D68}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
43 changes: 0 additions & 43 deletions src/NetFabric.Numerics.Angle.Benchmarks/MathBenchmarks.cs

This file was deleted.

45 changes: 0 additions & 45 deletions src/NetFabric.Numerics.Angle.Benchmarks/SumBenchmarks.cs

This file was deleted.

2 changes: 1 addition & 1 deletion src/NetFabric.Numerics.Angle/AngleAverage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public static partial class Angle
/// <summary>
/// Calculates the average of an collection of angles.
/// </summary>
/// <param name="source">The <see cref="ReadOnlyMemory{T}"/> of angles.</param>
/// <param name="source">The <see cref="IReadOnlyList{T}"/> of angles.</param>
/// <returns>The average angle in the collection, or null if the collection is empty.</returns>
/// <remarks>
/// The average angle is computed by summing all the angles in the given <paramref name="source"/> collection and dividing the sum by the count of angles.
Expand Down
36 changes: 16 additions & 20 deletions src/NetFabric.Numerics.Angle/AngleSum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public static Angle<TUnits, T> Sum<TUnits, T>(this IEnumerable<Angle<TUnits, T>>
where TUnits : IAngleUnits<TUnits>
where T : struct, IFloatingPoint<T>, IMinMaxValue<T>
{
if(Utils.TryGetSpan(source, out var span))
return span.Sum();

var sum = T.Zero;
foreach (var angle in source)
{
Expand Down Expand Up @@ -56,7 +59,7 @@ public static Angle<TUnits, T> Sum<TUnits, T>(this Memory<Angle<TUnits, T>> sour
/// <summary>
/// Calculates the sum of a collection of angles.
/// </summary>
/// <param name="source">The <see cref="ReadOnlyMemory{T}"/> collection of angles.</param>
/// <param name="source">The <see cref="IReadOnlyList{T}"/> collection of angles.</param>
/// <returns>The sum of the angles in the collection.</returns>
/// <remarks>
/// The sum of angles is computed by adding all the angles in the given <paramref name="source"/> collection.
Expand Down Expand Up @@ -94,30 +97,23 @@ public static Angle<TUnits, T> Sum<TUnits, T>(this ReadOnlySpan<Angle<TUnits, T>
where TUnits : IAngleUnits<TUnits>
where T : struct, IFloatingPoint<T>, IMinMaxValue<T>
{
return Vector.IsHardwareAccelerated && source.Length > Vector<T>.Count * 2
? new(AcceleratedSum(source))
: new(RegularSum(source));

static T RegularSum(ReadOnlySpan<Angle<TUnits, T>> source)
{
var sum = T.Zero;
foreach (var angle in source)
{
checked { sum += angle.Value; }
}
return sum;
}

static T AcceleratedSum(ReadOnlySpan<Angle<TUnits, T>> source)
var sum = T.Zero;
if(Vector.IsHardwareAccelerated && source.Length > Vector<T>.Count * 2)
{
var vectors = MemoryMarshal.Cast<Angle<TUnits, T>, Vector<T>>(source);
var sum = Vector<T>.Zero;
var sumVector = Vector<T>.Zero;

foreach (var vector in vectors)
sum += vector;
foreach (ref readonly var vector in vectors)
sumVector += vector;

sum = sumVector.SumItems();
var remainder = source.Length % Vector<T>.Count;
return sum.SumVectorItems() + RegularSum(source[^remainder..]);
source = source[^remainder..];
}
foreach (ref readonly var angle in source)
{
checked { sum += angle.Value; }
}
return new(sum);
}
}
10 changes: 5 additions & 5 deletions src/NetFabric.Numerics.Angle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ using NetFabric.Numerics.Angle;
var degreesDoubleAngle = new Angle<Degrees, double>(45.0);
var radiansFloatAngle = new Angle<Radians, float>(1.57f);
var gradiansDecimalAngle = new Angle<Gradians, decimal>(200.0m);
var revolutionsDoubleAngle = new Angle<Revolutions, Half>((Half)0.25);
var revolutionsHalfAngle = new Angle<Revolutions, Half>((Half)0.25);

// Constants
var zeroDegreesDoubleAngle = Angle<Degrees, double>.Zero; // 0.0 degrees
Expand All @@ -30,7 +30,7 @@ var quotient = gradiansDecimalAngle / 100.0m;
var remainder = degreesDoubleAngle % 180.0;

// Compare angles
var areEqual = degreesDoubleAngle.Equals(Angle.ToDegrees(revolutionsDoubleAngle));
var areEqual = degreesDoubleAngle.Equals(Angle<Gradians, double>.Right);
var isGreater = gradiansDecimalAngle > Angle<Gradians, decimal>.Right;

// Convert angles
Expand All @@ -46,7 +46,7 @@ var convertToFloatTruncating = Angle<Degrees, float>.CreateTruncating(degreesDou
var sineValue = Angle.Sin(radiansFloatAngle);
var cosineValue = Angle.Cos(Angle.ToRadians(degreesDoubleAngle));
var tangentValue = Angle.Tan(radiansFloatAngle);
var arcsineRadiansAngle = Angle.Asin(sineValue);
var arcSineRadiansAngle = Angle.Asin(sineValue);

// Reduce angles
var reducedAngle = Angle.Reduce(degreesDoubleAngle);
Expand All @@ -70,7 +70,7 @@ var collectionAverage = angleCollection.Average();

- `Angle<TUnits, T>` represents an angle as a value of type `T` in the specified `TUnits` unit.
- `AngleReduced<TUnits, T>` represents an angle as a value of type `T` in the specified `TUnits` unit, reduced to the range `[TUnits.Zero, TUnits.Full[`.
- The `T` type can be any of the following types: `float`, `double`, `decimal`, or any other implementation of `IFloatingPoint<TSelf>`.
- The `T` type can be any of the following types: `Half`, `float`, `double`, `decimal`, or any other implementation of `IFloatingPoint<TSelf>`.
- The `TUnits` type can be any of the following types: `Degrees`, `Radians`, `Gradians`, `Revolutions`, or any other implementation of `IAngleUnits<TSelf>`.

`Angle<TUnits, T>` can represent any angle value in the range `[T.MinValue, T.MaxValue]` in the specified `TUnits` unit. Some operations require the angle to be reduced to `[TUnits.Zero, TUnits.Full[`.
Expand All @@ -95,7 +95,7 @@ These methods are only available for angles in radians. When using an angle on a

**NetFabric.Numerics.Angle** provides optimized operations on collections of angles: `Sum`, `Average`.

These operations are available for `IEnumerable<Angle<TUnits, T>>`, `Angle<TUnits, T>[]`, `Memory<Angle<TUnits, T>>`, `ReadOnlyMemory<Angle<TUnits, T>>`, `Span<Angle<TUnits, T>>`, and `ReadOnlySpan<Angle<TUnits, T>>`.
These operations are available for `IEnumerable<Angle<TUnits, T>>`, `Angle<TUnits, T>[]`, `Memory<Angle<TUnits, T>>`, `IReadOnlyList<Angle<TUnits, T>>`, `Span<Angle<TUnits, T>>`, and `ReadOnlySpan<Angle<TUnits, T>>`.

These operations use SIMD instructions when possible, ensuring high-performance calculations.

Expand Down
36 changes: 36 additions & 0 deletions src/NetFabric.Numerics.Angle/Utils.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Runtime.InteropServices;

namespace NetFabric.Numerics
{
static class Utils
Expand Down Expand Up @@ -61,5 +63,39 @@ public static T Lerp<T, TFactor>(T a1, T a2, TFactor factor)
where TFactor : struct, INumberBase<TFactor>
=> (a1 * (TFactor.One - factor)) + (a2 * factor);
#endif

// Source: https://github.com/dotnet/dotnet/blob/f20eddc465ad40b7620e848b88a43fe6f6741e59/src/runtime/src/libraries/System.Linq/src/System/Linq/Enumerable.cs#L27
[MethodImpl(MethodImplOptions.AggressiveInlining)] // fast type checks that don't add a lot of overhead
public static bool TryGetSpan<TSource>(this IEnumerable<TSource> source, out ReadOnlySpan<TSource> span)
// This constraint isn't required, but the overheads involved here can be more substantial when TSource
// is a reference type and generic implementations are shared. So for now we're protecting ourselves
// and forcing a conscious choice to remove this in the future, at which point it should be paired with
// sufficient performance testing.
where TSource : struct
{
// Use `GetType() == typeof(...)` rather than `is` to avoid cast helpers. This is measurably cheaper
// but does mean we could end up missing some rare cases where we could get a span but don't (e.g. a uint[]
// masquerading as an int[]). That's an acceptable tradeoff. The Unsafe usage is only after we've
// validated the exact type; this could be changed to a cast in the future if the JIT starts to recognize it.
// We only pay the comparison/branching costs here for super common types we expect to be used frequently
// with LINQ methods.

bool result = true;
if (source.GetType() == typeof(TSource[]))
{
span = Unsafe.As<TSource[]>(source);
}
else if (source.GetType() == typeof(List<TSource>))
{
span = CollectionsMarshal.AsSpan(Unsafe.As<List<TSource>>(source));
}
else
{
span = default;
result = false;
}

return result;
}
}
}
4 changes: 2 additions & 2 deletions src/NetFabric.Numerics.Angle/VectorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

static class VectorExtensions
{
public static T SumVectorItems<T>(this Vector<T> vector)
public static T SumItems<T>(this Vector<T> vector)
where T : struct, IFloatingPoint<T>
{
ref var item = ref Unsafe.As<Vector<T>, T>(ref Unsafe.AsRef(in vector));
var sum = T.Zero;
ref var item = ref Unsafe.As<Vector<T>, T>(ref Unsafe.AsRef(in vector));
for (var index = 0; index < Vector<T>.Count; index++)
sum += Unsafe.Add(ref item, index);
return sum;
Expand Down
53 changes: 53 additions & 0 deletions src/NetFabric.Numerics.Benchmarks/MathBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using BenchmarkDotNet.Attributes;

namespace NetFabric.Numerics.Benchmarks;

public class MathBenchmarks
{
double[]? doubles;
Angle<Radians, double>[]? angles;

[Params(0, 1, 10, 1_000)]
public int Count { get; set; }


[GlobalSetup]
public void GlobalSetup()
{
doubles= new double[Count];
angles = new Angle<Radians, double>[Count];

for (var index = 0; index < Count; index++)
{
doubles![index] = index;
angles![index] = new Angle<Radians, double>(index);
}
}

[Benchmark(Baseline = true)]
public double Double()
{
var sum = 0.0;
foreach (var value in doubles!)
sum += value;
return sum;
}

[Benchmark]
public Angle<Radians, double> Angle()
{
Angle<Radians, double> sum = Angle<Radians, double>.Zero;
foreach (var angle in angles!)
sum += angle;
return sum;
}

[Benchmark]
public Angle<Radians, double> Mix()
{
var sum = 0.0;
foreach (var angle in angles!)
sum += angle.Value;
return new(sum);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
<PackageReference Include="NetFabric" Version="1.3.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading

0 comments on commit 31c43cc

Please sign in to comment.