diff --git a/.appveyor.yml b/.appveyor.yml index b03ac3d..d340d46 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,7 +1,7 @@ version: 0.1.{build}.0 pull_requests: do_not_increment_build_number: true -image: Visual Studio 2017 +image: Visual Studio 2017 Preview clone_depth: 1 clone_folder: c:\projects\gfoidl environment: diff --git a/.circleci/config.yml b/.circleci/config.yml index 0799b79..64bc8ef 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 defaults: &defaults working_directory: ~/repo docker: - - image: microsoft/dotnet:2.0.5-sdk-2.1.4 + - image: microsoft/dotnet:2.1-sdk environment: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1 diff --git a/gfoidl.Stochastics.sln b/gfoidl.Stochastics.sln index c17287c..df3b19b 100644 --- a/gfoidl.Stochastics.sln +++ b/gfoidl.Stochastics.sln @@ -15,6 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{D9E63A0E .appveyor.yml = .appveyor.yml build.sh = build.sh Directory.Build.props = Directory.Build.props + nuget.config = nuget.config EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "source", "source", "{0B4051AE-E1F2-4601-B0A8-B4CA660CC4B2}" diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..095110b --- /dev/null +++ b/nuget.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/perf/gfoidl.Stochastics.Benchmarks/gfoidl.Stochastics.Benchmarks.csproj b/perf/gfoidl.Stochastics.Benchmarks/gfoidl.Stochastics.Benchmarks.csproj index b3027c3..58dc48d 100644 --- a/perf/gfoidl.Stochastics.Benchmarks/gfoidl.Stochastics.Benchmarks.csproj +++ b/perf/gfoidl.Stochastics.Benchmarks/gfoidl.Stochastics.Benchmarks.csproj @@ -9,11 +9,11 @@ - netcoreapp2.0;net471 + netcoreapp2.1;netcoreapp2.0;net471 - netcoreapp2.0 + netcoreapp2.1;netcoreapp2.0 @@ -21,7 +21,10 @@ - + + + + diff --git a/source/gfoidl.Stochastics/Statistics/Sample.AverageVarianceCore.cs b/source/gfoidl.Stochastics/Statistics/Sample.AverageVarianceCore.cs index 948e560..8b8855d 100644 --- a/source/gfoidl.Stochastics/Statistics/Sample.AverageVarianceCore.cs +++ b/source/gfoidl.Stochastics/Statistics/Sample.AverageVarianceCore.cs @@ -101,7 +101,7 @@ private unsafe (double avg, double variance) CalculateAverageAndVarianceCoreImpl } // Reduction -- https://github.com/gfoidl/Stochastics/issues/43 - avg += Vector.Dot(avgVec, Vector.One); + avg += avgVec.ReduceSum(); } for (; i < n; ++i) diff --git a/source/gfoidl.Stochastics/Statistics/Sample.Delta.cs b/source/gfoidl.Stochastics/Statistics/Sample.Delta.cs index 3c6a657..f28c887 100644 --- a/source/gfoidl.Stochastics/Statistics/Sample.Delta.cs +++ b/source/gfoidl.Stochastics/Statistics/Sample.Delta.cs @@ -94,7 +94,7 @@ private unsafe double CalculateDeltaImpl((int start, int end) range) } // Reduction -- https://github.com/gfoidl/Stochastics/issues/43 - delta += Vector.Dot(deltaVec, Vector.One); + delta += deltaVec.ReduceSum(); } for (; i < n; ++i) diff --git a/source/gfoidl.Stochastics/Statistics/Sample.MinMax.cs b/source/gfoidl.Stochastics/Statistics/Sample.MinMax.cs index 1218dfd..75fc864 100644 --- a/source/gfoidl.Stochastics/Statistics/Sample.MinMax.cs +++ b/source/gfoidl.Stochastics/Statistics/Sample.MinMax.cs @@ -107,11 +107,7 @@ private unsafe (double min, double max) GetMinMaxImpl((int start, int end) range } // Reduction - for (int j = 0; j < Vector.Count; ++j) - { - if (minVec[j] < min) min = minVec[j]; - if (maxVec[j] > max) max = maxVec[j]; - } + VectorHelper.ReduceMinMax(minVec, maxVec, ref min, ref max); } for (; i < n; ++i) diff --git a/source/gfoidl.Stochastics/Statistics/Sample.SkewnessAndKurtosis.cs b/source/gfoidl.Stochastics/Statistics/Sample.SkewnessAndKurtosis.cs index b5266d0..8afd331 100644 --- a/source/gfoidl.Stochastics/Statistics/Sample.SkewnessAndKurtosis.cs +++ b/source/gfoidl.Stochastics/Statistics/Sample.SkewnessAndKurtosis.cs @@ -105,8 +105,8 @@ private unsafe (double skewness, double kurtosis) CalculateSkewnessAndKurtosisIm } // Reduction -- https://github.com/gfoidl/Stochastics/issues/43 - skewness += Vector.Dot(skewVec, Vector.One); - kurtosis += Vector.Dot(kurtVec, Vector.One); + skewness += skewVec.ReduceSum(); + kurtosis += kurtVec.ReduceSum(); } for (; i < n; ++i) diff --git a/source/gfoidl.Stochastics/ThrowHelper.cs b/source/gfoidl.Stochastics/ThrowHelper.cs index 127a341..c5a66ce 100644 --- a/source/gfoidl.Stochastics/ThrowHelper.cs +++ b/source/gfoidl.Stochastics/ThrowHelper.cs @@ -6,7 +6,7 @@ namespace gfoidl.Stochastics { internal static class ThrowHelper { -#if NETSTANDARD2_0 +#if !NET471 public static void ThrowArgumentNull(ExceptionArgument argument) => throw new ArgumentNullException(GetArgumentName(argument)); public static void ThrowArgumentOutOfRange(ExceptionArgument argument) => throw new ArgumentOutOfRangeException(GetArgumentName(argument)); public static void ThrowArgumentOutOfRange(ExceptionArgument argument, ExceptionResource resource) => throw new ArgumentOutOfRangeException(GetArgumentName(argument), GetResourceText(resource)); diff --git a/source/gfoidl.Stochastics/VectorHelper.cs b/source/gfoidl.Stochastics/VectorHelper.cs index 7ca165b..fed0887 100644 --- a/source/gfoidl.Stochastics/VectorHelper.cs +++ b/source/gfoidl.Stochastics/VectorHelper.cs @@ -3,6 +3,11 @@ using System.Numerics; using System.Runtime.CompilerServices; +#if NETCOREAPP2_1 +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +#endif + namespace gfoidl.Stochastics { [DebuggerNonUserCode] @@ -33,5 +38,67 @@ public static void WriteVectorWithAdvance(this Vector vector, ref double Unsafe.WriteUnaligned(arr, vector); arr += Vector.Count; } + //--------------------------------------------------------------------- + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double ReduceSum(this Vector vector) + { +#if NETCOREAPP2_1 + if (Avx.IsSupported && Sse2.IsSupported && 256 / 8 == sizeof(double) * Vector.Count) + { + Vector256 a = Unsafe.As, Vector256>(ref vector); + Vector256 tmp = Avx.HorizontalAdd(a, a); + Vector128 hi128 = Avx.ExtractVector128(tmp, 1); + Vector128 s = Sse2.Add(Unsafe.As, Vector128>(ref tmp), hi128); + + return Sse2.ConvertToDouble(s); + } +#endif + return Vector.Dot(Vector.One, vector); + } + //--------------------------------------------------------------------- + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReduceMinMax(Vector minVec, Vector maxVec, ref double min, ref double max) + { +#if NETCOREAPP2_1 + if (Avx.IsSupported && Sse2.IsSupported && 256 / 8 == sizeof(double) * Vector.Count) + { + min = MinMaxCore(minVec, true); + max = MinMaxCore(maxVec, false); + return; + } +#endif + for (int j = 0; j < Vector.Count; ++j) + { + if (minVec[j] < min) min = minVec[j]; + if (maxVec[j] > max) max = maxVec[j]; + } + } + //--------------------------------------------------------------------- +#if NETCOREAPP2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double MinMaxCore(Vector vector, bool doMin) + { + Vector256 vec256 = Unsafe.As, Vector256>(ref vector); + Vector128 hi128 = Avx.ExtractVector128(vec256, 1); + Vector128 lo128 = Avx.ExtractVector128(vec256, 0); + Vector128 tmp1 = Avx.Permute(hi128, 0b_01); + Vector128 tmp2 = Avx.Permute(lo128, 0b_01); + + if (doMin) + { + hi128 = Sse2.Min(hi128, tmp1); + lo128 = Sse2.Min(lo128, tmp2); + lo128 = Sse2.Min(lo128, hi128); + } + else + { + hi128 = Sse2.Max(hi128, tmp1); + lo128 = Sse2.Max(lo128, tmp2); + lo128 = Sse2.Max(lo128, hi128); + } + + return Sse2.ConvertToDouble(lo128); + } +#endif } -} \ No newline at end of file +} diff --git a/source/gfoidl.Stochastics/gfoidl.Stochastics.csproj b/source/gfoidl.Stochastics/gfoidl.Stochastics.csproj index 610ecbf..1a45f6c 100644 --- a/source/gfoidl.Stochastics/gfoidl.Stochastics.csproj +++ b/source/gfoidl.Stochastics/gfoidl.Stochastics.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netstandard2.0;netcoreapp2.1 latest true @@ -19,11 +19,16 @@ bin\Release\netstandard2.0\gfoidl.Stochastics.xml - + + + + + + diff --git a/tests/gfoidl.Stochastics.Tests/VectorHelperTests/ReduceMinMax.cs b/tests/gfoidl.Stochastics.Tests/VectorHelperTests/ReduceMinMax.cs new file mode 100644 index 0000000..075b4cb --- /dev/null +++ b/tests/gfoidl.Stochastics.Tests/VectorHelperTests/ReduceMinMax.cs @@ -0,0 +1,40 @@ +using System; +using System.Numerics; +using NUnit.Framework; + +namespace gfoidl.Stochastics.Tests.VectorHelperTests +{ + [TestFixture] + public class ReduceMinMax + { + [Test, Repeat(10)] + public void Min_Max_Vectors_given___correct_Min_Max() + { + var rnd = new Random(); + var minArr = new double[Vector.Count]; + var maxArr = new double[Vector.Count]; + double min = double.MaxValue; + double max = double.MinValue; + + for (int j = 0; j < Vector.Count; ++j) + { + minArr[j] = rnd.NextDouble(); + maxArr[j] = rnd.NextDouble(); + + if (minArr[j] < min) min = minArr[j]; + if (maxArr[j] > max) max = maxArr[j]; + } + + double expectedMin = min; + double expectedMax = max; + + var minVec = new Vector(minArr); + var maxVec = new Vector(maxArr); + + VectorHelper.ReduceMinMax(minVec, maxVec, ref min, ref max); + + Assert.AreEqual(expectedMin, min, 1e-10); + Assert.AreEqual(expectedMax, max, 1e-10); + } + } +} diff --git a/tests/gfoidl.Stochastics.Tests/VectorHelperTests/ReduceSum.cs b/tests/gfoidl.Stochastics.Tests/VectorHelperTests/ReduceSum.cs new file mode 100644 index 0000000..1d1ef9d --- /dev/null +++ b/tests/gfoidl.Stochastics.Tests/VectorHelperTests/ReduceSum.cs @@ -0,0 +1,37 @@ +using System; +using System.Numerics; +using NUnit.Framework; + +namespace gfoidl.Stochastics.Tests.VectorHelperTests +{ + [TestFixture] + public class ReduceSum + { + [Test, Repeat(10)] + public void Random_Vector_given___Correct_Sum_Reduction() + { + if (Vector.IsHardwareAccelerated) + { + var rnd = new Random(); + var arr = new double[Vector.Count]; + double expected = 0; + + for (int j = 0; j < arr.Length; ++j) + { + arr[j] = rnd.NextDouble(); + expected += arr[j]; + } + + var vector = new Vector(arr); + + double actual = vector.ReduceSum(); + + Assert.AreEqual(expected, actual, 1e-10); + } + else + { + Assert.Ignore("Vector.IsHardwareAccelerated is false"); + } + } + } +} diff --git a/tests/gfoidl.Stochastics.Tests/gfoidl.Stochastics.Tests.csproj b/tests/gfoidl.Stochastics.Tests/gfoidl.Stochastics.Tests.csproj index 662c611..97d1535 100644 --- a/tests/gfoidl.Stochastics.Tests/gfoidl.Stochastics.Tests.csproj +++ b/tests/gfoidl.Stochastics.Tests/gfoidl.Stochastics.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp2.0 + netcoreapp2.1;netcoreapp2.0