Skip to content

Commit

Permalink
Merge pull request #49 from gfoidl/simd-reduction
Browse files Browse the repository at this point in the history
Better Simd reduction
  • Loading branch information
gfoidl authored Apr 19, 2018
2 parents bf00c42 + 1a2a603 commit cc76c50
Show file tree
Hide file tree
Showing 15 changed files with 174 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions gfoidl.Stochastics.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
6 changes: 6 additions & 0 deletions nuget.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="dotnet-myget" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
</packageSources>
</configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,22 @@
</PropertyGroup>

<PropertyGroup Condition="'$(OS)' == 'Windows_NT'">
<TargetFrameworks>netcoreapp2.0;net471</TargetFrameworks>
<TargetFrameworks>netcoreapp2.1;netcoreapp2.0;net471</TargetFrameworks>
</PropertyGroup>

<PropertyGroup Condition="'$(OS)' != 'Windows_NT'">
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFrameworks>netcoreapp2.1;netcoreapp2.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<Content Include="..\..\native-out\*" Visible="false" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.10.11" />
<PackageReference Include="BenchmarkDotNet" Version="0.10.11" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.0'">
<PackageReference Include="System.Memory" Version="4.4.0-preview2-25405-01" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ private unsafe (double avg, double variance) CalculateAverageAndVarianceCoreImpl
}

// Reduction -- https://github.com/gfoidl/Stochastics/issues/43
avg += Vector.Dot(avgVec, Vector<double>.One);
avg += avgVec.ReduceSum();
}

for (; i < n; ++i)
Expand Down
2 changes: 1 addition & 1 deletion source/gfoidl.Stochastics/Statistics/Sample.Delta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<double>.One);
delta += deltaVec.ReduceSum();
}

for (; i < n; ++i)
Expand Down
6 changes: 1 addition & 5 deletions source/gfoidl.Stochastics/Statistics/Sample.MinMax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,7 @@ private unsafe (double min, double max) GetMinMaxImpl((int start, int end) range
}

// Reduction
for (int j = 0; j < Vector<double>.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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ private unsafe (double skewness, double kurtosis) CalculateSkewnessAndKurtosisIm
}

// Reduction -- https://github.com/gfoidl/Stochastics/issues/43
skewness += Vector.Dot(skewVec, Vector<double>.One);
kurtosis += Vector.Dot(kurtVec, Vector<double>.One);
skewness += skewVec.ReduceSum();
kurtosis += kurtVec.ReduceSum();
}

for (; i < n; ++i)
Expand Down
2 changes: 1 addition & 1 deletion source/gfoidl.Stochastics/ThrowHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
69 changes: 68 additions & 1 deletion source/gfoidl.Stochastics/VectorHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -33,5 +38,67 @@ public static void WriteVectorWithAdvance(this Vector<double> vector, ref double
Unsafe.WriteUnaligned(arr, vector);
arr += Vector<double>.Count;
}
//---------------------------------------------------------------------
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double ReduceSum(this Vector<double> vector)
{
#if NETCOREAPP2_1
if (Avx.IsSupported && Sse2.IsSupported && 256 / 8 == sizeof(double) * Vector<double>.Count)
{
Vector256<double> a = Unsafe.As<Vector<double>, Vector256<double>>(ref vector);
Vector256<double> tmp = Avx.HorizontalAdd(a, a);
Vector128<double> hi128 = Avx.ExtractVector128(tmp, 1);
Vector128<double> s = Sse2.Add(Unsafe.As<Vector256<double>, Vector128<double>>(ref tmp), hi128);

return Sse2.ConvertToDouble(s);
}
#endif
return Vector.Dot(Vector<double>.One, vector);
}
//---------------------------------------------------------------------
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ReduceMinMax(Vector<double> minVec, Vector<double> maxVec, ref double min, ref double max)
{
#if NETCOREAPP2_1
if (Avx.IsSupported && Sse2.IsSupported && 256 / 8 == sizeof(double) * Vector<double>.Count)
{
min = MinMaxCore(minVec, true);
max = MinMaxCore(maxVec, false);
return;
}
#endif
for (int j = 0; j < Vector<double>.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<double> vector, bool doMin)
{
Vector256<double> vec256 = Unsafe.As<Vector<double>, Vector256<double>>(ref vector);
Vector128<double> hi128 = Avx.ExtractVector128(vec256, 1);
Vector128<double> lo128 = Avx.ExtractVector128(vec256, 0);
Vector128<double> tmp1 = Avx.Permute(hi128, 0b_01);
Vector128<double> 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
}
}
}
9 changes: 7 additions & 2 deletions source/gfoidl.Stochastics/gfoidl.Stochastics.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0;netcoreapp2.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
Expand All @@ -19,11 +19,16 @@
<DocumentationFile>bin\Release\netstandard2.0\gfoidl.Stochastics.xml</DocumentationFile>
</PropertyGroup>

<ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp2.1'">
<PackageReference Include="System.Numerics.Vectors" Version="4.4.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.0-preview3-26417-03" />
<PackageReference Include="System.Runtime.Intrinsics.Experimental" Version="4.5.0-preview2-26406-04" />
</ItemGroup>

<ItemGroup>
<Content Include="..\..\native-out\gfoidl-Stochastics-Native.dll" PackagePath="runtimes/win-x64/native" Visible="false" CopyToOutputDirectory="PreserveNewest" />
<Content Include="..\..\native-out\libgfoidl-Stochastics-Native.so" PackagePath="runtimes/linux-x64/native" Visible="false" CopyToOutputDirectory="PreserveNewest" />
Expand Down
40 changes: 40 additions & 0 deletions tests/gfoidl.Stochastics.Tests/VectorHelperTests/ReduceMinMax.cs
Original file line number Diff line number Diff line change
@@ -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<double>.Count];
var maxArr = new double[Vector<double>.Count];
double min = double.MaxValue;
double max = double.MinValue;

for (int j = 0; j < Vector<double>.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<double>(minArr);
var maxVec = new Vector<double>(maxArr);

VectorHelper.ReduceMinMax(minVec, maxVec, ref min, ref max);

Assert.AreEqual(expectedMin, min, 1e-10);
Assert.AreEqual(expectedMax, max, 1e-10);
}
}
}
37 changes: 37 additions & 0 deletions tests/gfoidl.Stochastics.Tests/VectorHelperTests/ReduceSum.cs
Original file line number Diff line number Diff line change
@@ -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<double>.Count];
double expected = 0;

for (int j = 0; j < arr.Length; ++j)
{
arr[j] = rnd.NextDouble();
expected += arr[j];
}

var vector = new Vector<double>(arr);

double actual = vector.ReduceSum();

Assert.AreEqual(expected, actual, 1e-10);
}
else
{
Assert.Ignore("Vector.IsHardwareAccelerated is false");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFrameworks>netcoreapp2.1;netcoreapp2.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit cc76c50

Please sign in to comment.