diff --git a/Microsoft.ML.sln b/Microsoft.ML.sln index c9be0e726b..9d969b6be2 100644 --- a/Microsoft.ML.sln +++ b/Microsoft.ML.sln @@ -256,6 +256,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.DataView", " pkg\Microsoft.Data.DataView\Microsoft.Data.DataView.symbols.nupkgproj = pkg\Microsoft.Data.DataView\Microsoft.Data.DataView.symbols.nupkgproj EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RemoteExecutorConsoleApp", "test\RemoteExecutorConsoleApp\RemoteExecutorConsoleApp.csproj", "{5E920CAC-5A28-42FB-936E-49C472130953}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -914,6 +916,18 @@ Global {85D0CAFD-2FE8-496A-88C7-585D35B94243}.Release-Intrinsics|Any CPU.Build.0 = Release-Intrinsics|Any CPU {85D0CAFD-2FE8-496A-88C7-585D35B94243}.Release-netfx|Any CPU.ActiveCfg = Release-netfx|Any CPU {85D0CAFD-2FE8-496A-88C7-585D35B94243}.Release-netfx|Any CPU.Build.0 = Release-netfx|Any CPU + {5E920CAC-5A28-42FB-936E-49C472130953}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E920CAC-5A28-42FB-936E-49C472130953}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E920CAC-5A28-42FB-936E-49C472130953}.Debug-Intrinsics|Any CPU.ActiveCfg = Debug-Intrinsics|Any CPU + {5E920CAC-5A28-42FB-936E-49C472130953}.Debug-Intrinsics|Any CPU.Build.0 = Debug-Intrinsics|Any CPU + {5E920CAC-5A28-42FB-936E-49C472130953}.Debug-netfx|Any CPU.ActiveCfg = Debug-netfx|Any CPU + {5E920CAC-5A28-42FB-936E-49C472130953}.Debug-netfx|Any CPU.Build.0 = Debug-netfx|Any CPU + {5E920CAC-5A28-42FB-936E-49C472130953}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E920CAC-5A28-42FB-936E-49C472130953}.Release|Any CPU.Build.0 = Release|Any CPU + {5E920CAC-5A28-42FB-936E-49C472130953}.Release-Intrinsics|Any CPU.ActiveCfg = Release-Intrinsics|Any CPU + {5E920CAC-5A28-42FB-936E-49C472130953}.Release-Intrinsics|Any CPU.Build.0 = Release-Intrinsics|Any CPU + {5E920CAC-5A28-42FB-936E-49C472130953}.Release-netfx|Any CPU.ActiveCfg = Release-netfx|Any CPU + {5E920CAC-5A28-42FB-936E-49C472130953}.Release-netfx|Any CPU.Build.0 = Release-netfx|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -996,6 +1010,7 @@ Global {A84717CB-F11A-41C5-A74D-C0F1D47B7431} = {D3D38B03-B557-484D-8348-8BADEE4DF592} {85D0CAFD-2FE8-496A-88C7-585D35B94243} = {09EADF06-BE25-4228-AB53-95AE3E15B530} {31D38B21-102B-41C0-9E0A-2FE0BF68D123} = {D3D38B03-B557-484D-8348-8BADEE4DF592} + {5E920CAC-5A28-42FB-936E-49C472130953} = {AED9C836-31E3-4F3F-8ABC-929555D3F3C4} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {41165AF1-35BB-4832-A189-73060F82B01D} diff --git a/test/Directory.Build.props b/test/Directory.Build.props index b708cba96e..1aa4c973c7 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -6,6 +6,8 @@ net461 netcoreapp3.0 win-x64 + + $(AllowedReferenceRelatedFileExtensions);.runtimeconfig.json;.runtimeconfig.dev.json;.deps.json false true diff --git a/test/Microsoft.ML.Benchmarks/Harness/Configs.cs b/test/Microsoft.ML.Benchmarks/Harness/Configs.cs index c40c1e76e2..fcc4b5d419 100644 --- a/test/Microsoft.ML.Benchmarks/Harness/Configs.cs +++ b/test/Microsoft.ML.Benchmarks/Harness/Configs.cs @@ -38,7 +38,7 @@ protected virtual Job GetJobDefinition() /// private IToolchain CreateToolchain() { -#if NET461 +#if NETFRAMEWORK var tfm = "net461"; var csProj = CsProjClassicNetToolchain.Net461; #else diff --git a/test/Microsoft.ML.Benchmarks/Harness/ProjectGenerator.cs b/test/Microsoft.ML.Benchmarks/Harness/ProjectGenerator.cs index b4987a09c9..32d5e5e5e0 100644 --- a/test/Microsoft.ML.Benchmarks/Harness/ProjectGenerator.cs +++ b/test/Microsoft.ML.Benchmarks/Harness/ProjectGenerator.cs @@ -29,7 +29,7 @@ public class ProjectGenerator : CsProjGenerator public ProjectGenerator(string targetFrameworkMoniker) : base(targetFrameworkMoniker, null, null, null) { -#if NET461 +#if NETFRAMEWORK runtimeIdentifier = "win-x64"; #endif } diff --git a/test/Microsoft.ML.Benchmarks/Microsoft.ML.Benchmarks.csproj b/test/Microsoft.ML.Benchmarks/Microsoft.ML.Benchmarks.csproj index 0cc163e1fb..81516c3eb8 100644 --- a/test/Microsoft.ML.Benchmarks/Microsoft.ML.Benchmarks.csproj +++ b/test/Microsoft.ML.Benchmarks/Microsoft.ML.Benchmarks.csproj @@ -2,7 +2,7 @@ Exe 7.2 - Microsoft.ML.Benchmarks.Program + false diff --git a/test/Microsoft.ML.CpuMath.PerformanceTests/Microsoft.ML.CpuMath.PerformanceTests.csproj b/test/Microsoft.ML.CpuMath.PerformanceTests/Microsoft.ML.CpuMath.PerformanceTests.csproj index ccd6712fd3..5c7f587879 100644 --- a/test/Microsoft.ML.CpuMath.PerformanceTests/Microsoft.ML.CpuMath.PerformanceTests.csproj +++ b/test/Microsoft.ML.CpuMath.PerformanceTests/Microsoft.ML.CpuMath.PerformanceTests.csproj @@ -2,7 +2,7 @@ Exe 7.2 - Microsoft.ML.CpuMath.PerformanceTests.Program + false netcoreapp3.0 true diff --git a/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/Microsoft.ML.CpuMath.UnitTests.netcoreapp.csproj b/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/Microsoft.ML.CpuMath.UnitTests.netcoreapp.csproj index a94c11f5ef..b0c85309fb 100644 --- a/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/Microsoft.ML.CpuMath.UnitTests.netcoreapp.csproj +++ b/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/Microsoft.ML.CpuMath.UnitTests.netcoreapp.csproj @@ -6,6 +6,7 @@ + diff --git a/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/UnitTests.cs b/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/UnitTests.cs index ee9ba74ea8..e68694a234 100644 --- a/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/UnitTests.cs +++ b/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/UnitTests.cs @@ -5,22 +5,31 @@ using System; using System.Collections.Generic; using Microsoft.ML.Internal.CpuMath; +using Microsoft.ML.TestFramework; using Xunit; namespace Microsoft.ML.CpuMath.UnitTests { public class CpuMathUtilsUnitTests { - private readonly float[][] _testArrays; - private readonly int[] _testIndexArray; - private readonly AlignedArray[] _testMatrices; - private readonly AlignedArray[] _testSrcVectors; - private readonly AlignedArray[] _testDstVectors; - private readonly int _vectorAlignment = CpuMathUtils.GetVectorAlignment(); - private readonly FloatEqualityComparer _comparer; - private readonly FloatEqualityComparerForMatMul _matMulComparer; - - public CpuMathUtilsUnitTests() + private static readonly float[][] _testArrays; + private static readonly int[] _testIndexArray; + private static readonly AlignedArray[] _testMatrices; + private static readonly AlignedArray[] _testSrcVectors; + private static readonly AlignedArray[] _testDstVectors; + private static readonly int _vectorAlignment = CpuMathUtils.GetVectorAlignment(); + private static readonly FloatEqualityComparer _comparer; + private static readonly FloatEqualityComparerForMatMul _matMulComparer; + private static readonly string defaultMode = "defaultMode"; +#if NETCOREAPP3_0 + private static Dictionary DisableAvxEnvironmentVariables; + private static Dictionary DisableAvxAndSseEnvironmentVariables; + private static readonly string disableAvx = "COMPlus_EnableAvx"; + private static readonly string disableSse = "COMPlus_EnableSse"; + private static readonly string disableAvxAndSse = "COMPlus_EnableHWIntrinsic"; +#endif + + static CpuMathUtilsUnitTests() { // Padded array whose length is a multiple of 4 float[] testArray1 = new float[16] { 1.96f, -2.38f, -9.76f, 13.84f, -106.37f, -26.93f, 32.45f, 3.29f, 1.96f, -2.38f, -9.76f, 13.84f, -106.37f, -26.93f, 32.45f, 3.29f }; @@ -75,433 +84,681 @@ public CpuMathUtilsUnitTests() testDstVectorAligned2.CopyFrom(testDstVector2); _testDstVectors = new AlignedArray[] { testDstVectorAligned1, testDstVectorAligned2 }; + +#if NETCOREAPP3_0 + DisableAvxEnvironmentVariables = new Dictionary() + { + { disableAvx , "0" } + }; + + DisableAvxAndSseEnvironmentVariables = new Dictionary() + { + { disableAvx , "0" }, + { disableSse , "0" } + }; +#endif } + private static void CheckProperFlag(string mode) + { +#if NETCOREAPP3_0 + if (mode == defaultMode) + { + Assert.True(System.Runtime.Intrinsics.X86.Avx.IsSupported); + Assert.True(System.Runtime.Intrinsics.X86.Sse.IsSupported); + } + else if (mode == disableAvx) + { + Assert.False(System.Runtime.Intrinsics.X86.Avx.IsSupported); + Assert.True(System.Runtime.Intrinsics.X86.Sse.IsSupported); + } + else if (mode == disableAvxAndSse) + { + Assert.False(System.Runtime.Intrinsics.X86.Avx.IsSupported); + Assert.False(System.Runtime.Intrinsics.X86.Sse.IsSupported); + } +#endif + } + + public static TheoryData> AddData() => new TheoryData>() + { + { defaultMode, "0", null }, + { defaultMode, "1", null }, + +#if NETCOREAPP3_0 + { disableAvx, "0", DisableAvxEnvironmentVariables }, + { disableAvx, "1", DisableAvxEnvironmentVariables }, + + { disableAvxAndSse, "0", DisableAvxAndSseEnvironmentVariables }, + { disableAvxAndSse, "1", DisableAvxAndSseEnvironmentVariables }, +#endif + }; + + public static TheoryData> AddScaleData() => new TheoryData>() + { + { defaultMode, "0", "1.7", null }, + { defaultMode, "1", "1.7", null }, + { defaultMode, "0", "-1.7", null }, + { defaultMode, "1", "-1.7", null }, + +#if NETCOREAPP3_0 + { disableAvx, "0", "1.7", DisableAvxEnvironmentVariables }, + { disableAvx, "1", "1.7", DisableAvxEnvironmentVariables }, + { disableAvx, "0", "-1.7", DisableAvxEnvironmentVariables }, + { disableAvx, "1", "-1.7", DisableAvxEnvironmentVariables }, + + { disableAvxAndSse, "0", "1.7", DisableAvxAndSseEnvironmentVariables }, + { disableAvxAndSse, "1", "1.7", DisableAvxAndSseEnvironmentVariables }, + { disableAvxAndSse, "0", "-1.7", DisableAvxAndSseEnvironmentVariables }, + { disableAvxAndSse, "1", "-1.7", DisableAvxAndSseEnvironmentVariables }, +#endif + }; + + public static TheoryData> MatMulData => new TheoryData>() + { + { defaultMode, "0", "0", "0", null }, + { defaultMode, "1", "1", "0", null }, + { defaultMode, "1", "0", "1", null }, +#if NETCOREAPP3_0 + { disableAvx, "0", "0", "0", DisableAvxEnvironmentVariables }, + { disableAvx, "1", "1", "0", DisableAvxEnvironmentVariables }, + { disableAvx, "1", "0", "1", DisableAvxEnvironmentVariables }, + + { disableAvxAndSse , "0", "0", "0", DisableAvxAndSseEnvironmentVariables }, + { disableAvxAndSse , "1", "1", "0", DisableAvxAndSseEnvironmentVariables }, + { disableAvxAndSse , "1", "0", "1", DisableAvxAndSseEnvironmentVariables }, +#endif + }; + [Theory] - [InlineData(0, 0, 0, new float[] { -416.6801f, -416.6801f, -416.6801f, -416.6801f, -416.6801f, -416.6801f, -416.6801f, -416.6801f })] - [InlineData(1, 1, 0, new float[] { 1496f, 3672f, 5848f, 8024f, 10200f, 12376f, 14552f, 16728f })] - [InlineData(1, 0, 1, new float[] { 204f, 492f, 780f, 1068f, 1356f, 1644f, 1932f, 2220f, 2508f, 2796f, 3084f, 3372f, 3660f, 3948f, 4236f, 4524f })] - public void MatMulTest(int matTest, int srcTest, int dstTest, float[] expected) + [MemberData(nameof(MatMulData))] + public void MatMulTest(string mode, string matTest, string srcTest, string dstTest, Dictionary environmentVariables) { - AlignedArray mat = _testMatrices[matTest]; - AlignedArray src = _testSrcVectors[srcTest]; - AlignedArray dst = _testDstVectors[dstTest]; + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2, arg3) => + { + CheckProperFlag(arg0); + AlignedArray mat = _testMatrices[int.Parse(arg1)]; + AlignedArray src = _testSrcVectors[int.Parse(arg2)]; + AlignedArray dst = _testDstVectors[int.Parse(arg3)]; - CpuMathUtils.MatrixTimesSource(false, mat, src, dst, dst.Size); - float[] actual = new float[dst.Size]; - dst.CopyTo(actual, 0, dst.Size); - Assert.Equal(expected, actual, _matMulComparer); + float[] expected = new float[dst.Size]; + for (int i = 0; i < dst.Size; i++) + { + float dotProduct = 0; + for (int j = 0; j < src.Size; j++) + { + dotProduct += mat[i * src.Size + j] * src[j]; + } + expected[i] = dotProduct; + } + + CpuMathUtils.MatrixTimesSource(false, mat, src, dst, dst.Size); + float[] actual = new float[dst.Size]; + dst.CopyTo(actual, 0, dst.Size); + Assert.Equal(expected, actual, _matMulComparer); + return RemoteExecutor.SuccessExitCode; + }, mode, matTest, srcTest, dstTest, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 0, 0, new float[] { 70.56001f, -85.68f, -351.36f, 498.24f, -3829.32f, -969.48f, 1168.2f, 118.44f })] - [InlineData(1, 0, 1, new float[] { 2724f, 2760f, 2796f, 2832f, 2868f, 2904f, 2940f, 2976f, 3012f, 3048f, 3084f, 3120f, 3156f, 3192f, 3228f, 3264f })] - [InlineData(1, 1, 0, new float[] { 11016f, 11152f, 11288f, 11424f, 11560f, 11696f, 11832f, 11968f })] - public void MatMulTranTest(int matTest, int srcTest, int dstTest, float[] expected) + [MemberData(nameof(MatMulData))] + public void MatMulTranTest(string mode, string matTest, string srcTest, string dstTest, Dictionary environmentVariables) { - AlignedArray mat = _testMatrices[matTest]; - AlignedArray src = _testSrcVectors[srcTest]; - AlignedArray dst = _testDstVectors[dstTest]; + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2, arg3) => + { + CheckProperFlag(arg0); + AlignedArray mat = _testMatrices[int.Parse(arg1)]; + AlignedArray src = _testSrcVectors[int.Parse(arg2)]; + AlignedArray dst = _testDstVectors[int.Parse(arg3)]; - CpuMathUtils.MatrixTimesSource(true, mat, src, dst, src.Size); - float[] actual = new float[dst.Size]; - dst.CopyTo(actual, 0, dst.Size); - Assert.Equal(expected, actual, _matMulComparer); + float[] expected = new float[dst.Size]; + for (int i = 0; i < dst.Size; i++) + { + float dotProduct = 0; + for (int j = 0; j < src.Size; j++) + { + dotProduct += mat[j * dst.Size + i] * src[j]; + } + expected[i] = dotProduct; + } + + CpuMathUtils.MatrixTimesSource(true, mat, src, dst, src.Size); + float[] actual = new float[dst.Size]; + dst.CopyTo(actual, 0, dst.Size); + Assert.Equal(expected, actual, _matMulComparer); + return RemoteExecutor.SuccessExitCode; + }, mode, matTest, srcTest, dstTest, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 0, 0, new float[] { 38.25002f, 38.25002f, 38.25002f, 38.25002f, 38.25002f, 38.25002f, 38.25002f, 38.25002f })] - [InlineData(1, 1, 0, new float[] { 910f, 2190f, 3470f, 4750f, 6030f, 7310f, 8590f, 9870f })] - [InlineData(1, 0, 1, new float[] { 95f, 231f, 367f, 503f, 639f, 775f, 911f, 1047f, 1183f, 1319f, 1455f, 1591f, 1727f, 1863f, 1999f, 2135f })] - public void MatTimesSrcSparseTest(int matTest, int srcTest, int dstTest, float[] expected) + [MemberData(nameof(MatMulData))] + public void MatTimesSrcSparseTest(string mode, string matTest, string srcTest, string dstTest, Dictionary environmentVariables) { - AlignedArray mat = _testMatrices[matTest]; - AlignedArray src = _testSrcVectors[srcTest]; - AlignedArray dst = _testDstVectors[dstTest]; - int[] idx = _testIndexArray; + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2, arg3) => + { + CheckProperFlag(arg0); + AlignedArray mat = _testMatrices[int.Parse(arg1)]; + AlignedArray src = _testSrcVectors[int.Parse(arg2)]; + AlignedArray dst = _testDstVectors[int.Parse(arg3)]; + int[] idx = _testIndexArray; + + float[] expected = new float[dst.Size]; + int limit = (int.Parse(arg2) == 0) ? 4 : 9; + for (int i = 0; i < dst.Size; i++) + { + float dotProduct = 0; + for (int j = 0; j < limit; j++) + { + int col = idx[j]; + dotProduct += mat[i * src.Size + col] * src[col]; + } + expected[i] = dotProduct; + } + + CpuMathUtils.MatrixTimesSource(mat, idx, src, 0, 0, limit, dst, dst.Size); + float[] actual = new float[dst.Size]; + dst.CopyTo(actual, 0, dst.Size); + Assert.Equal(expected, actual, _matMulComparer); + return RemoteExecutor.SuccessExitCode; - CpuMathUtils.MatrixTimesSource(mat, idx, src, 0, 0, (srcTest == 0) ? 4 : 9, dst, dst.Size); - float[] actual = new float[dst.Size]; - dst.CopyTo(actual, 0, dst.Size); - Assert.Equal(expected, actual, _matMulComparer); + }, mode, matTest, srcTest, dstTest, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 1.7f)] - [InlineData(1, 1.7f)] - [InlineData(0, -1.7f)] - [InlineData(1, -1.7f)] - public void AddScalarUTest(int test, float defaultScale) + [MemberData(nameof(AddScaleData))] + public void AddScalarUTest(string mode, string test, string scale, Dictionary environmentVariables) { - float[] dst = (float[])_testArrays[test].Clone(); - float[] expected = (float[])dst.Clone(); - - for (int i = 0; i < expected.Length; i++) + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) => { - expected[i] += defaultScale; - } + CheckProperFlag(arg0); + float defaultScale = float.Parse(arg2); + float[] dst = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] expected = (float[])dst.Clone(); - CpuMathUtils.Add(defaultScale, dst); - var actual = dst; - Assert.Equal(expected, actual, _comparer); + for (int i = 0; i < expected.Length; i++) + { + expected[i] += defaultScale; + } + + CpuMathUtils.Add(defaultScale, dst); + var actual = dst; + Assert.Equal(expected, actual, _comparer); + return RemoteExecutor.SuccessExitCode; + }, mode, test, scale, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 1.7f)] - [InlineData(1, 1.7f)] - [InlineData(0, -1.7f)] - [InlineData(1, -1.7f)] - public void ScaleTest(int test, float defaultScale) + [MemberData(nameof(AddScaleData))] + public void ScaleTest(string mode, string test, string scale, Dictionary environmentVariables) { - float[] dst = (float[])_testArrays[test].Clone(); - float[] expected = (float[])dst.Clone(); - - for (int i = 0; i < expected.Length; i++) + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) => { - expected[i] *= defaultScale; - } + CheckProperFlag(arg0); + float defaultScale = float.Parse(arg2); + float[] dst = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] expected = (float[])dst.Clone(); - CpuMathUtils.Scale(defaultScale, dst); - var actual = dst; - Assert.Equal(expected, actual, _comparer); + for (int i = 0; i < expected.Length; i++) + { + expected[i] *= defaultScale; + } + + CpuMathUtils.Scale(defaultScale, dst); + var actual = dst; + Assert.Equal(expected, actual, _comparer); + return RemoteExecutor.SuccessExitCode; + }, mode, test, scale, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 1.7f)] - [InlineData(1, 1.7f)] - [InlineData(0, -1.7f)] - [InlineData(1, -1.7f)] - public void ScaleSrcUTest(int test, float defaultScale) + [MemberData(nameof(AddScaleData))] + public void ScaleSrcUTest(string mode, string test, string scale, Dictionary environmentVariables) { - float[] src = (float[])_testArrays[test].Clone(); - float[] dst = (float[])src.Clone(); - float[] expected = (float[])dst.Clone(); - - for (int i = 0; i < expected.Length; i++) + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) => { - expected[i] *= defaultScale; - } + CheckProperFlag(arg0); + float defaultScale = float.Parse(arg2); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] dst = (float[])src.Clone(); + float[] expected = (float[])dst.Clone(); - CpuMathUtils.Scale(defaultScale, src, dst, dst.Length); - var actual = dst; - Assert.Equal(expected, actual, _comparer); + for (int i = 0; i < expected.Length; i++) + { + expected[i] *= defaultScale; + } + + CpuMathUtils.Scale(defaultScale, src, dst, dst.Length); + var actual = dst; + Assert.Equal(expected, actual, _comparer); + return RemoteExecutor.SuccessExitCode; + }, mode, test, scale, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 1.7f)] - [InlineData(1, 1.7f)] - [InlineData(0, -1.7f)] - [InlineData(1, -1.7f)] - public void ScaleAddUTest(int test, float defaultScale) + [MemberData(nameof(AddScaleData))] + public void ScaleAddUTest(string mode, string test, string scale, Dictionary environmentVariables) { - float[] dst = (float[])_testArrays[test].Clone(); - float[] expected = (float[])dst.Clone(); - - for (int i = 0; i < expected.Length; i++) + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) => { - expected[i] = defaultScale * (dst[i] + defaultScale); - } + CheckProperFlag(arg0); + float defaultScale = float.Parse(arg2); + float[] dst = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] expected = (float[])dst.Clone(); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = defaultScale * (dst[i] + defaultScale); + } + + CpuMathUtils.ScaleAdd(defaultScale, defaultScale, dst); + var actual = dst; + Assert.Equal(expected, actual, _comparer); + return RemoteExecutor.SuccessExitCode; + }, mode, test, scale, new RemoteInvokeOptions(environmentVariables)); - CpuMathUtils.ScaleAdd(defaultScale, defaultScale, dst); - var actual = dst; - Assert.Equal(expected, actual, _comparer); } [Theory] - [InlineData(0, 1.7f)] - [InlineData(1, 1.7f)] - [InlineData(0, -1.7f)] - [InlineData(1, -1.7f)] - public void AddScaleUTest(int test, float defaultScale) + [MemberData(nameof(AddScaleData))] + public void AddScaleUTest(string mode, string test, string scale, Dictionary environmentVariables) { - float[] src = (float[])_testArrays[test].Clone(); - float[] dst = (float[])src.Clone(); - float[] expected = (float[])dst.Clone(); - - for (int i = 0; i < expected.Length; i++) + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) => { - expected[i] *= (1 + defaultScale); - } + CheckProperFlag(arg0); + float defaultScale = float.Parse(arg2); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] dst = (float[])src.Clone(); + float[] expected = (float[])dst.Clone(); - CpuMathUtils.AddScale(defaultScale, src, dst, dst.Length); - var actual = dst; - Assert.Equal(expected, actual, _comparer); + for (int i = 0; i < expected.Length; i++) + { + expected[i] *= (1 + defaultScale); + } + + CpuMathUtils.AddScale(defaultScale, src, dst, dst.Length); + var actual = dst; + Assert.Equal(expected, actual, _comparer); + return RemoteExecutor.SuccessExitCode; + }, mode, test, scale, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 1.7f)] - [InlineData(1, 1.7f)] - [InlineData(0, -1.7f)] - [InlineData(1, -1.7f)] - public void AddScaleSUTest(int test, float defaultScale) - { - float[] src = (float[])_testArrays[test].Clone(); - float[] dst = (float[])src.Clone(); - int[] idx = _testIndexArray; - float[] expected = (float[])dst.Clone(); - - CpuMathUtils.AddScale(defaultScale, src, idx, dst, idx.Length); - for (int i = 0; i < idx.Length; i++) + [MemberData(nameof(AddScaleData))] + public void AddScaleSUTest(string mode, string test, string scale, Dictionary environmentVariables) + { + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) => { - int index = idx[i]; - expected[index] += defaultScale * src[i]; - } + CheckProperFlag(arg0); + float defaultScale = float.Parse(arg2); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] dst = (float[])src.Clone(); + int[] idx = _testIndexArray; + float[] expected = (float[])dst.Clone(); + + CpuMathUtils.AddScale(defaultScale, src, idx, dst, idx.Length); + for (int i = 0; i < idx.Length; i++) + { + int index = idx[i]; + expected[index] += defaultScale * src[i]; + } - Assert.Equal(expected, dst, _comparer); + Assert.Equal(expected, dst, _comparer); + return RemoteExecutor.SuccessExitCode; + }, mode, test, scale, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 1.7f)] - [InlineData(1, 1.7f)] - [InlineData(0, -1.7f)] - [InlineData(1, -1.7f)] - public void AddScaleCopyUTest(int test, float defaultScale) - { - float[] src = (float[])_testArrays[test].Clone(); - float[] dst = (float[])src.Clone(); - float[] result = (float[])dst.Clone(); - float[] expected = (float[])dst.Clone(); - - for (int i = 0; i < expected.Length; i++) + [MemberData(nameof(AddScaleData))] + public void AddScaleCopyUTest(string mode, string test, string scale, Dictionary environmentVariables) + { + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) => { - expected[i] *= (1 + defaultScale); - } + CheckProperFlag(arg0); + float defaultScale = float.Parse(arg2); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] dst = (float[])src.Clone(); + float[] result = (float[])dst.Clone(); + float[] expected = (float[])dst.Clone(); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] *= (1 + defaultScale); + } - CpuMathUtils.AddScaleCopy(defaultScale, src, dst, result, dst.Length); - var actual = result; - Assert.Equal(expected, actual, _comparer); + CpuMathUtils.AddScaleCopy(defaultScale, src, dst, result, dst.Length); + var actual = result; + Assert.Equal(expected, actual, _comparer); + return RemoteExecutor.SuccessExitCode; + }, mode, test, scale, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0)] - [InlineData(1)] - public void AddUTest(int test) + [MemberData(nameof(AddData))] + public void AddUTest(string mode, string test, Dictionary environmentVariables) { - float[] src = (float[])_testArrays[test].Clone(); - float[] dst = (float[])src.Clone(); - float[] expected = (float[])src.Clone(); - - // Ensures src and dst are different arrays - for (int i = 0; i < dst.Length; i++) + RemoteExecutor.RemoteInvoke((arg0, arg1) => { - dst[i] += 1; - } + CheckProperFlag(arg0); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] dst = (float[])src.Clone(); + float[] expected = (float[])src.Clone(); - for (int i = 0; i < expected.Length; i++) - { - expected[i] = 2 * expected[i] + 1; - } + // Ensures src and dst are different arrays + for (int i = 0; i < dst.Length; i++) + { + dst[i] += 1; + } - CpuMathUtils.Add(src, dst, dst.Length); - var actual = dst; - Assert.Equal(expected, actual, _comparer); + for (int i = 0; i < expected.Length; i++) + { + expected[i] = 2 * expected[i] + 1; + } + + CpuMathUtils.Add(src, dst, dst.Length); + var actual = dst; + Assert.Equal(expected, actual, _comparer); + return RemoteExecutor.SuccessExitCode; + }, mode, test, new RemoteInvokeOptions (environmentVariables)); } [Theory] - [InlineData(0)] - [InlineData(1)] - public void AddSUTest(int test) - { - float[] src = (float[])_testArrays[test].Clone(); - float[] dst = (float[])src.Clone(); - int[] idx = _testIndexArray; - float[] expected = (float[])dst.Clone(); - - expected[0] = 3.92f; - expected[2] = -12.14f; - expected[5] = -36.69f; - expected[6] = 46.29f; - expected[8] = -104.41f; - expected[11] = -13.09f; - expected[12] = -73.92f; - expected[13] = -23.64f; - expected[14] = 34.41f; - - CpuMathUtils.Add(src, idx, dst, idx.Length); - var actual = dst; - Assert.Equal(expected, actual, _comparer); + [MemberData(nameof(AddData))] + public void AddSUTest(string mode, string test, Dictionary environmentVariables) + { + RemoteExecutor.RemoteInvoke((arg0, arg1) => + { + CheckProperFlag(arg0); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] dst = (float[])src.Clone(); + int[] idx = _testIndexArray; + float[] expected = (float[])dst.Clone(); + + expected[0] = 3.92f; + expected[2] = -12.14f; + expected[5] = -36.69f; + expected[6] = 46.29f; + expected[8] = -104.41f; + expected[11] = -13.09f; + expected[12] = -73.92f; + expected[13] = -23.64f; + expected[14] = 34.41f; + + CpuMathUtils.Add(src, idx, dst, idx.Length); + var actual = dst; + Assert.Equal(expected, actual, _comparer); + return RemoteExecutor.SuccessExitCode; + }, mode, test, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0)] - [InlineData(1)] - public void MulElementWiseUTest(int test) + [MemberData(nameof(AddData))] + public void MulElementWiseUTest(string mode, string test, Dictionary environmentVariables) { - float[] src1 = (float[])_testArrays[test].Clone(); - float[] src2 = (float[])src1.Clone(); - float[] dst = (float[])src1.Clone(); - - // Ensures src1 and src2 are different arrays - for (int i = 0; i < src2.Length; i++) + RemoteExecutor.RemoteInvoke((arg0, arg1) => { - src2[i] += 1; - } + CheckProperFlag(arg1); + float[] src1 = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] src2 = (float[])src1.Clone(); + float[] dst = (float[])src1.Clone(); - float[] expected = (float[])src1.Clone(); + // Ensures src1 and src2 are different arrays + for (int i = 0; i < src2.Length; i++) + { + src2[i] += 1; + } - for (int i = 0; i < expected.Length; i++) - { - expected[i] *= (1 + expected[i]); - } + float[] expected = (float[])src1.Clone(); - CpuMathUtils.MulElementWise(src1, src2, dst, dst.Length); - var actual = dst; - Assert.Equal(expected, actual, _comparer); + for (int i = 0; i < expected.Length; i++) + { + expected[i] *= (1 + expected[i]); + } + + CpuMathUtils.MulElementWise(src1, src2, dst, dst.Length); + var actual = dst; + Assert.Equal(expected, actual, _comparer); + return RemoteExecutor.SuccessExitCode; + }, mode, test, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, -187.8f)] - [InlineData(1, -191.09f)] - public void SumTest(int test, float expected) + [MemberData(nameof(AddData))] + public void SumTest(string mode, string test, Dictionary environmentVariables) { - float[] src = (float[])_testArrays[test].Clone(); - var actual = CpuMathUtils.Sum(src); - Assert.Equal(expected, actual, 2); + RemoteExecutor.RemoteInvoke((arg0, arg1) => + { + CheckProperFlag(arg0); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float expected = 0; + for (int i = 0; i < src.Length; i++) + { + expected += src[i]; + } + + var actual = CpuMathUtils.Sum(src); + Assert.Equal(expected, actual, 2); + return RemoteExecutor.SuccessExitCode; + }, mode, test, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 26799.8752f)] - [InlineData(1, 26789.0511f)] - public void SumSqUTest(int test, float expected) + [MemberData(nameof(AddData))] + public void SumSqUTest(string mode, string test, Dictionary environmentVariables) { - float[] src = (float[])_testArrays[test].Clone(); - var actual = CpuMathUtils.SumSq(src); - Assert.Equal(expected, actual, 2); + RemoteExecutor.RemoteInvoke((arg0, arg1) => + { + CheckProperFlag(arg0); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float expected = 0; + for (int i = 0; i < src.Length; i++) + { + expected += src[i] * src[i]; + } + + var actual = CpuMathUtils.SumSq(src); + Assert.Equal(expected, actual, 2); + return RemoteExecutor.SuccessExitCode; + }, mode, test, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 1.7f)] - [InlineData(1, 1.7f)] - [InlineData(0, -1.7f)] - [InlineData(1, -1.7f)] - public void SumSqDiffUTest(int test, float defaultScale) + [MemberData(nameof(AddScaleData))] + public void SumSqDiffUTest(string mode, string test, string scale, Dictionary environmentVariables) { - float[] src = (float[])_testArrays[test].Clone(); - var actual = CpuMathUtils.SumSq(defaultScale, src); - - float expected = 0; - for (int i = 0; i < src.Length; i++) + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) => { - expected += (src[i] - defaultScale) * (src[i] - defaultScale); - } + CheckProperFlag(arg0); + float defaultScale = float.Parse(arg2); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + var actual = CpuMathUtils.SumSq(defaultScale, src); - Assert.Equal(expected, actual, 2); + float expected = 0; + for (int i = 0; i < src.Length; i++) + { + expected += (src[i] - defaultScale) * (src[i] - defaultScale); + } + + Assert.Equal(expected, actual, 2); + return RemoteExecutor.SuccessExitCode; + }, mode, test, scale, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 393.96f)] - [InlineData(1, 390.67f)] - public void SumAbsUTest(int test, float expected) + [MemberData(nameof(AddData))] + public void SumAbsUTest(string mode, string test, Dictionary environmentVariables) { - float[] src = (float[])_testArrays[test].Clone(); - var actual = CpuMathUtils.SumAbs(src); - Assert.Equal(expected, actual, 2); + RemoteExecutor.RemoteInvoke((arg0, arg1) => + { + CheckProperFlag(arg0); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float expected = 0; + for (int i = 0; i < src.Length; i++) + { + expected += Math.Abs(src[i]); + } + + var actual = CpuMathUtils.SumAbs(src); + Assert.Equal(expected, actual, 2); + return RemoteExecutor.SuccessExitCode; + }, mode, test, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 1.7f)] - [InlineData(1, 1.7f)] - [InlineData(0, -1.7f)] - [InlineData(1, -1.7f)] - public void SumAbsDiffUTest(int test, float defaultScale) + [MemberData(nameof(AddScaleData))] + public void SumAbsDiffUTest(string mode, string test, string scale, Dictionary environmentVariables) { - float[] src = (float[])_testArrays[test].Clone(); - var actual = CpuMathUtils.SumAbs(defaultScale, src); - - float expected = 0; - for (int i = 0; i < src.Length; i++) + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) => { - expected += Math.Abs(src[i] - defaultScale); - } + CheckProperFlag(arg0); + float defaultScale = float.Parse(arg2); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + var actual = CpuMathUtils.SumAbs(defaultScale, src); + + float expected = 0; + for (int i = 0; i < src.Length; i++) + { + expected += Math.Abs(src[i] - defaultScale); + } - Assert.Equal(expected, actual, 2); + Assert.Equal(expected, actual, 2); + return RemoteExecutor.SuccessExitCode; + }, mode, test, scale, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 106.37f)] - [InlineData(1, 106.37f)] - public void MaxAbsUTest(int test, float expected) + [MemberData(nameof(AddData))] + public void MaxAbsUTest(string mode, string test, Dictionary environmentVariables) { - float[] src = (float[])_testArrays[test].Clone(); - var actual = CpuMathUtils.MaxAbs(src); - Assert.Equal(expected, actual, 2); + RemoteExecutor.RemoteInvoke((arg0, arg1) => + { + CheckProperFlag(arg0); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + var actual = CpuMathUtils.MaxAbs(src); + + float expected = 0; + for (int i = 0; i < src.Length; i++) + { + float abs = Math.Abs(src[i]); + if (abs > expected) + { + expected = abs; + } + } + + Assert.Equal(expected, actual, 2); + return RemoteExecutor.SuccessExitCode; + }, mode, test, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 1.7f)] - [InlineData(1, 1.7f)] - [InlineData(0, -1.7f)] - [InlineData(1, -1.7f)] - public void MaxAbsDiffUTest(int test, float defaultScale) + [MemberData(nameof(AddScaleData))] + public void MaxAbsDiffUTest(string mode, string test, string scale, Dictionary environmentVariables) { - float[] src = (float[])_testArrays[test].Clone(); - var actual = CpuMathUtils.MaxAbsDiff(defaultScale, src); - - float expected = 0; - for (int i = 0; i < src.Length; i++) + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) => { - float abs = Math.Abs(src[i] - defaultScale); - if (abs > expected) + CheckProperFlag(arg0); + float defaultScale = float.Parse(arg2); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + var actual = CpuMathUtils.MaxAbsDiff(defaultScale, src); + + float expected = 0; + for (int i = 0; i < src.Length; i++) { - expected = abs; + float abs = Math.Abs(src[i] - defaultScale); + if (abs > expected) + { + expected = abs; + } } - } - - Assert.Equal(expected, actual, 2); + Assert.Equal(expected, actual, 2); + return RemoteExecutor.SuccessExitCode; + }, mode, test, scale, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 26612.0752f)] - [InlineData(1, 26597.9611f)] - public void DotUTest(int test, float expected) + [MemberData(nameof(AddData))] + public void DotUTest(string mode, string test, Dictionary environmentVariables) { - float[] src = (float[])_testArrays[test].Clone(); - float[] dst = (float[])src.Clone(); - - for (int i = 0; i < dst.Length; i++) + RemoteExecutor.RemoteInvoke((arg0, arg1) => { - dst[i] += 1; - } + CheckProperFlag(arg0); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] dst = (float[])src.Clone(); + + for (int i = 0; i < dst.Length; i++) + { + dst[i] += 1; + } - var actual = CpuMathUtils.DotProductDense(src, dst, dst.Length); - Assert.Equal(expected, actual, 1); + float expected = 0; + for (int i = 0; i < dst.Length; i++) + { + expected += src[i] * dst[i]; + } + + var actual = CpuMathUtils.DotProductDense(src, dst, dst.Length); + Assert.Equal(expected, actual, 1); + return RemoteExecutor.SuccessExitCode; + }, mode, test, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, -3406.2154f)] - [InlineData(1, -3406.2154f)] - public void DotSUTest(int test, float expected) + [MemberData(nameof(AddData))] + public void DotSUTest(string mode, string test, Dictionary environmentVariables) { - float[] src = (float[])_testArrays[test].Clone(); - float[] dst = (float[])src.Clone(); - int[] idx = _testIndexArray; - - // Ensures src and dst are different arrays - for (int i = 0; i < dst.Length; i++) + RemoteExecutor.RemoteInvoke((arg0, arg1) => { - dst[i] += 1; - } + CheckProperFlag(arg0); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] dst = (float[])src.Clone(); + int[] idx = _testIndexArray; - var actual = CpuMathUtils.DotProductSparse(src, dst, idx, idx.Length); - Assert.Equal(expected, actual, 2); + // Ensures src and dst are different arrays + for (int i = 0; i < dst.Length; i++) + { + dst[i] += 1; + } + + float expected = 0; + for (int i = 0; i < idx.Length; i++) + { + int index = idx[i]; + expected += src[index] * dst[i]; + } + + var actual = CpuMathUtils.DotProductSparse(src, dst, idx, idx.Length); + Assert.Equal(expected, actual, 2); + return RemoteExecutor.SuccessExitCode; + }, mode, test, new RemoteInvokeOptions(environmentVariables)); } [Theory] - [InlineData(0, 16.0f)] - [InlineData(1, 15.0f)] - public void Dist2Test(int test, float expected) + [MemberData(nameof(AddData))] + public void Dist2Test(string mode, string test, Dictionary environmentVariables) { - float[] src = (float[])_testArrays[test].Clone(); - float[] dst = (float[])src.Clone(); - - // Ensures src and dst are different arrays - for (int i = 0; i < dst.Length; i++) + RemoteExecutor.RemoteInvoke((arg0, arg1) => { - dst[i] += 1; - } + CheckProperFlag(arg0); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] dst = (float[])src.Clone(); + + // Ensures src and dst are different arrays + for (int i = 0; i < dst.Length; i++) + { + dst[i] += 1; + } - var actual = CpuMathUtils.L2DistSquared(src, dst, dst.Length); - Assert.Equal(expected, actual, 0); + float expected = 0; + for (int i = 0; i < dst.Length; i++) + { + float distance = src[i] - dst[i]; + expected += distance * distance; + } + + var actual = CpuMathUtils.L2DistSquared(src, dst, dst.Length); + Assert.Equal(expected, actual, 0); + return RemoteExecutor.SuccessExitCode; + }, mode, test, new RemoteInvokeOptions(environmentVariables)); } [Theory] @@ -533,51 +790,58 @@ public void ZeroMatrixItemsCoreTest(int test, int[] idx, float[] expected) } [Theory] - [InlineData(0, 1.7f)] - [InlineData(1, 1.7f)] - [InlineData(0, -1.7f)] - [InlineData(1, -1.7f)] - public void SdcaL1UpdateUTest(int test, float defaultScale) - { - float[] src = (float[])_testArrays[test].Clone(); - float[] v = (float[])src.Clone(); - float[] w = (float[])src.Clone(); - float[] expected = (float[])w.Clone(); - - for (int i = 0; i < expected.Length; i++) + [MemberData(nameof(AddScaleData))] + public void SdcaL1UpdateUTest(string mode, string test, string scale, Dictionary environmentVariables) + { + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) => { - float value = src[i] * (1 + defaultScale); - expected[i] = Math.Abs(value) > defaultScale ? (value > 0 ? value - defaultScale : value + defaultScale) : 0; - } + CheckProperFlag(arg0); + float defaultScale = float.Parse(arg2); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] v = (float[])src.Clone(); + float[] w = (float[])src.Clone(); + float[] expected = (float[])w.Clone(); + + for (int i = 0; i < expected.Length; i++) + { + float value = src[i] * (1 + defaultScale); + expected[i] = Math.Abs(value) > defaultScale ? (value > 0 ? value - defaultScale : value + defaultScale) : 0; + } - CpuMathUtils.SdcaL1UpdateDense(defaultScale, src.Length, src, defaultScale, v, w); - var actual = w; - Assert.Equal(expected, actual, _comparer); + CpuMathUtils.SdcaL1UpdateDense(defaultScale, src.Length, src, defaultScale, v, w); + var actual = w; + Assert.Equal(expected, actual, _comparer); + return RemoteExecutor.SuccessExitCode; + }, mode, test, scale, new RemoteInvokeOptions(environmentVariables)); } + [Theory] - [InlineData(0, 1.7f)] - [InlineData(1, 1.7f)] - [InlineData(0, -1.7f)] - [InlineData(1, -1.7f)] - public void SdcaL1UpdateSUTest(int test, float defaultScale) - { - float[] src = (float[])_testArrays[test].Clone(); - float[] v = (float[])src.Clone(); - float[] w = (float[])src.Clone(); - int[] idx = _testIndexArray; - float[] expected = (float[])w.Clone(); - - for (int i = 0; i < idx.Length; i++) + [MemberData(nameof(AddScaleData))] + public void SdcaL1UpdateSUTest(string mode, string test, string scale, Dictionary environmentVariables) + { + RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) => { - int index = idx[i]; - float value = v[index] + src[i] * defaultScale; - expected[index] = Math.Abs(value) > defaultScale ? (value > 0 ? value - defaultScale : value + defaultScale) : 0; - } + CheckProperFlag(arg0); + float defaultScale = float.Parse(arg2); + float[] src = (float[])_testArrays[int.Parse(arg1)].Clone(); + float[] v = (float[])src.Clone(); + float[] w = (float[])src.Clone(); + int[] idx = _testIndexArray; + float[] expected = (float[])w.Clone(); + + for (int i = 0; i < idx.Length; i++) + { + int index = idx[i]; + float value = v[index] + src[i] * defaultScale; + expected[index] = Math.Abs(value) > defaultScale ? (value > 0 ? value - defaultScale : value + defaultScale) : 0; + } - CpuMathUtils.SdcaL1UpdateSparse(defaultScale, idx.Length, src, idx, defaultScale, v, w); - var actual = w; - Assert.Equal(expected, actual, _comparer); + CpuMathUtils.SdcaL1UpdateSparse(defaultScale, idx.Length, src, idx, defaultScale, v, w); + var actual = w; + Assert.Equal(expected, actual, _comparer); + return RemoteExecutor.SuccessExitCode; + }, mode, test, scale, new RemoteInvokeOptions(environmentVariables)); } } diff --git a/test/Microsoft.ML.CpuMath.UnitTests.netstandard/Microsoft.ML.CpuMath.UnitTests.netstandard.csproj b/test/Microsoft.ML.CpuMath.UnitTests.netstandard/Microsoft.ML.CpuMath.UnitTests.netstandard.csproj index 00ba8546b1..55e79969a5 100644 --- a/test/Microsoft.ML.CpuMath.UnitTests.netstandard/Microsoft.ML.CpuMath.UnitTests.netstandard.csproj +++ b/test/Microsoft.ML.CpuMath.UnitTests.netstandard/Microsoft.ML.CpuMath.UnitTests.netstandard.csproj @@ -2,6 +2,7 @@ + diff --git a/test/Microsoft.ML.TestFramework/BaseTestBaseline.cs b/test/Microsoft.ML.TestFramework/BaseTestBaseline.cs index 67bc430575..b3c2faf3be 100644 --- a/test/Microsoft.ML.TestFramework/BaseTestBaseline.cs +++ b/test/Microsoft.ML.TestFramework/BaseTestBaseline.cs @@ -34,7 +34,7 @@ public abstract partial class BaseTestBaseline : BaseTestClass protected BaseTestBaseline(ITestOutputHelper output) : base(output) { -#if NET461 +#if NETFRAMEWORK NotFullFramework = false; LessThanNetCore30AndNotFullFramework = false; #endif diff --git a/test/Microsoft.ML.TestFramework/BaseTestClass.cs b/test/Microsoft.ML.TestFramework/BaseTestClass.cs index 3d8f1795a1..fbb85bcf2f 100644 --- a/test/Microsoft.ML.TestFramework/BaseTestClass.cs +++ b/test/Microsoft.ML.TestFramework/BaseTestClass.cs @@ -26,7 +26,7 @@ static BaseTestClass() private static string GetRepoRoot() { -#if NET461 +#if NETFRAMEWORK string directory = AppDomain.CurrentDomain.BaseDirectory; #else string directory = AppContext.BaseDirectory; @@ -51,7 +51,7 @@ public BaseTestClass(ITestOutputHelper output) //correct results that are on en-US locale. Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); -#if NET461 +#if NETFRAMEWORK // Substring is removing initial "file:///" from the codebase string. var currentAssemblyLocation = new FileInfo(Directory.GetParent(typeof(BaseTestClass).Assembly.CodeBase.Substring(8)).FullName); #else diff --git a/test/Microsoft.ML.TestFramework/Microsoft.ML.TestFramework.csproj b/test/Microsoft.ML.TestFramework/Microsoft.ML.TestFramework.csproj index f355684c18..a8a0ad7000 100644 --- a/test/Microsoft.ML.TestFramework/Microsoft.ML.TestFramework.csproj +++ b/test/Microsoft.ML.TestFramework/Microsoft.ML.TestFramework.csproj @@ -16,6 +16,7 @@ + diff --git a/test/Microsoft.ML.TestFramework/PasteArguments.cs b/test/Microsoft.ML.TestFramework/PasteArguments.cs new file mode 100644 index 0000000000..5898815d11 --- /dev/null +++ b/test/Microsoft.ML.TestFramework/PasteArguments.cs @@ -0,0 +1,152 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.ML.TestFramework +{ + internal static partial class PasteArguments + { + internal static void AppendArgument(StringBuilder stringBuilder, string argument) + { + if (stringBuilder.Length != 0) + { + stringBuilder.Append(' '); + } + + // Parsing rules for non-argv[0] arguments: + // - Backslash is a normal character except followed by a quote. + // - 2N backslashes followed by a quote ==> N literal backslashes followed by unescaped quote + // - 2N+1 backslashes followed by a quote ==> N literal backslashes followed by a literal quote + // - Parsing stops at first whitespace outside of quoted region. + // - (post 2008 rule): A closing quote followed by another quote ==> literal quote, and parsing remains in quoting mode. + if (argument.Length != 0 && ContainsNoWhitespaceOrQuotes(argument)) + { + // Simple case - no quoting or changes needed. + stringBuilder.Append(argument); + } + else + { + stringBuilder.Append(Quote); + int idx = 0; + while (idx < argument.Length) + { + char c = argument[idx++]; + if (c == Backslash) + { + int numBackSlash = 1; + while (idx < argument.Length && argument[idx] == Backslash) + { + idx++; + numBackSlash++; + } + + if (idx == argument.Length) + { + // We'll emit an end quote after this so must double the number of backslashes. + stringBuilder.Append(Backslash, numBackSlash * 2); + } + else if (argument[idx] == Quote) + { + // Backslashes will be followed by a quote. Must double the number of backslashes. + stringBuilder.Append(Backslash, numBackSlash * 2 + 1); + stringBuilder.Append(Quote); + idx++; + } + else + { + // Backslash will not be followed by a quote, so emit as normal characters. + stringBuilder.Append(Backslash, numBackSlash); + } + + continue; + } + + if (c == Quote) + { + // Escape the quote so it appears as a literal. This also guarantees that we won't end up generating a closing quote followed + // by another quote (which parses differently pre-2008 vs. post-2008.) + stringBuilder.Append(Backslash); + stringBuilder.Append(Quote); + continue; + } + + stringBuilder.Append(c); + } + + stringBuilder.Append(Quote); + } + } + + private static bool ContainsNoWhitespaceOrQuotes(string s) + { + for (int i = 0; i < s.Length; i++) + { + char c = s[i]; + if (char.IsWhiteSpace(c) || c == Quote) + { + return false; + } + } + + return true; + } + + private const char Quote = '\"'; + private const char Backslash = '\\'; + /// + /// Repastes a set of arguments into a linear string that parses back into the originals under pre- or post-2008 VC parsing rules. + /// The rules for parsing the executable name (argv[0]) are special, so you must indicate whether the first argument actually is argv[0]. + /// + internal static string Paste(IEnumerable arguments, bool pasteFirstArgumentUsingArgV0Rules) + { + var stringBuilder = new StringBuilder(); + + foreach (string argument in arguments) + { + if (pasteFirstArgumentUsingArgV0Rules) + { + pasteFirstArgumentUsingArgV0Rules = false; + + // Special rules for argv[0] + // - Backslash is a normal character. + // - Quotes used to include whitespace characters. + // - Parsing ends at first whitespace outside quoted region. + // - No way to get a literal quote past the parser. + + bool hasWhitespace = false; + foreach (char c in argument) + { + if (c == Quote) + { + throw new ApplicationException("The argv[0] argument cannot include a double quote."); + } + if (char.IsWhiteSpace(c)) + { + hasWhitespace = true; + } + } + if (argument.Length == 0 || hasWhitespace) + { + stringBuilder.Append(Quote); + stringBuilder.Append(argument); + stringBuilder.Append(Quote); + } + else + { + stringBuilder.Append(argument); + } + } + else + { + AppendArgument(stringBuilder, argument); + } + } + + return stringBuilder.ToString(); + } + } +} diff --git a/test/Microsoft.ML.TestFramework/RemoteExecutor.cs b/test/Microsoft.ML.TestFramework/RemoteExecutor.cs new file mode 100644 index 0000000000..17c4d6e15a --- /dev/null +++ b/test/Microsoft.ML.TestFramework/RemoteExecutor.cs @@ -0,0 +1,204 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Xunit; +using Xunit.Sdk; + +namespace Microsoft.ML.TestFramework +{ + /// + /// Base class used for all tests that need to spawn a remote process. + /// Most of the code has been taken from RemoteExecutorTestBase class in the corefx repo. + /// + public static class RemoteExecutor + { + /// The name of the test console app. + public static readonly string TestConsoleApp = Path.GetFullPath(@"RemoteExecutorConsoleApp.dll"); +#if NETFRAMEWORK + public static readonly string HostRunner = Path.GetFullPath(@"RemoteExecutorConsoleApp.exe"); + private static readonly string ExtraParameter = ""; +#else + public static readonly string HostRunner = Process.GetCurrentProcess().MainModule.FileName; + private static readonly string ExtraParameter = TestConsoleApp; +#endif + /// A timeout (milliseconds) after which a wait on a remote operation should be considered a failure. + public const int FailWaitTimeoutMilliseconds = 60 * 1000; + + /// The exit code returned when the test process exits successfully. + public const int SuccessExitCode = 42; + + /// Invokes the method from this assembly in another process using the specified arguments. + /// The method to invoke. + /// The first argument to pass to the method. + /// The second argument to pass to the method. + /// Options to use for the invocation. + public static void RemoteInvoke( + Func method, + string arg1, string arg2, + RemoteInvokeOptions options = null) + { + RemoteInvoke(GetMethodInfo(method), new[] { arg1, arg2 }, options); + } + + /// Invokes the method from this assembly in another process using the specified arguments. + /// The method to invoke. + /// The first argument to pass to the method. + /// The second argument to pass to the method. + /// The third argument to pass to the method. + /// Options to use for the invocation. + public static void RemoteInvoke( + Func method, + string arg1, string arg2, string arg3, + RemoteInvokeOptions options = null) + { + RemoteInvoke(GetMethodInfo(method), new[] { arg1, arg2, arg3 }, options); + } + + /// Invokes the method from this assembly in another process using the specified arguments. + /// The method to invoke. + /// The first argument to pass to the method. + /// The second argument to pass to the method. + /// The third argument to pass to the method. + /// The fourth argument to pass to the method. + /// Options to use for the invocation. + public static void RemoteInvoke( + Func method, + string arg1, string arg2, string arg3, string arg4, + RemoteInvokeOptions options = null) + { + RemoteInvoke(GetMethodInfo(method), new[] { arg1, arg2, arg3, arg4 }, options); + } + + /// Invokes the method from this assembly in another process using the specified arguments. + /// The method to invoke. + /// The arguments to pass to the method. + /// Options to use for the invocation. + /// true if this function should paste the arguments (e.g. surrounding with quotes); false if that responsibility is left up to the caller. + private static void RemoteInvoke(MethodInfo method, string[] args, RemoteInvokeOptions options, bool pasteArguments = true) + { + options = options ?? new RemoteInvokeOptions(); + + // Verify the specified method returns an int (the exit code) or nothing, + // and that if it accepts any arguments, they're all strings. + Assert.True(method.ReturnType == typeof(void) || method.ReturnType == typeof(int) || method.ReturnType == typeof(Task)); + Assert.All(method.GetParameters(), pi => Assert.Equal(typeof(string), pi.ParameterType)); + + // And make sure it's in this assembly. This isn't critical, but it helps with deployment to know + // that the method to invoke is available because we're already running in this assembly. + Type t = method.DeclaringType; + Assembly a = t.GetTypeInfo().Assembly; + + // Start the other process and return a wrapper for it to handle its lifetime and exit checking. + ProcessStartInfo psi = options.StartInfo; + psi.UseShellExecute = false; + + // If we need the host (if it exists), use it, otherwise target the console app directly. + string metadataArgs = PasteArguments.Paste(new string[] { a.FullName, t.FullName, method.Name, options.ExceptionFile }, pasteFirstArgumentUsingArgV0Rules: false); + string passedArgs = pasteArguments ? PasteArguments.Paste(args, pasteFirstArgumentUsingArgV0Rules: false) : string.Join(" ", args); + string testConsoleAppArgs = ExtraParameter + " " + metadataArgs + " " + passedArgs; + + psi.FileName = HostRunner; + psi.Arguments = testConsoleAppArgs; + + // Return the handle to the process, which may or not be started + CheckProcess(Process.Start(psi), options); + } + + private static void CheckProcess(Process process, RemoteInvokeOptions Options) + { + if (process != null) + { + // A bit unorthodox to do throwing operations in a Dispose, but by doing it here we avoid + // needing to do this in every derived test and keep each test much simpler. + try + { + Assert.True(process.WaitForExit(Options.TimeOut), + $"Timed out after {Options.TimeOut}ms waiting for remote process {process.Id}"); + + if (File.Exists(Options.ExceptionFile)) + { + throw new RemoteExecutionException(File.ReadAllText(Options.ExceptionFile)); + } + + if (Options.CheckExitCode) + { + int expected = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Options.ExpectedExitCode : unchecked((sbyte)Options.ExpectedExitCode); + int actual = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? process.ExitCode : unchecked((sbyte)process.ExitCode); + + Assert.True(expected == actual, $"Exit code was {process.ExitCode} but it should have been {Options.ExpectedExitCode}"); + } + } + finally + { + if (File.Exists(Options.ExceptionFile)) + { + File.Delete(Options.ExceptionFile); + } + + // Cleanup + try { process.Kill(); } + catch { } // ignore all cleanup errors + + process.Dispose(); + process = null; + } + } + } + + private sealed class RemoteExecutionException : XunitException + { + internal RemoteExecutionException(string stackTrace) : base("Remote process failed with an unhandled exception.", stackTrace) { } + } + + private static MethodInfo GetMethodInfo(Delegate d) + { + // RemoteInvoke doesn't support marshaling state on classes associated with + // the delegate supplied (often a display class of a lambda). If such fields + // are used, odd errors result, e.g. NullReferenceExceptions during the remote + // execution. Try to ward off the common cases by proactively failing early + // if it looks like such fields are needed. + if (d.Target != null) + { + // The only fields on the type should be compiler-defined (any fields of the compiler's own + // making generally include '<' and '>', as those are invalid in C# source). Note that this logic + // may need to be revised in the future as the compiler changes, as this relies on the specifics of + // actually how the compiler handles lifted fields for lambdas. + Type targetType = d.Target.GetType(); + Assert.All( + targetType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fi => Assert.True(fi.Name.IndexOf('<') != -1, $"Field marshaling is not supported by {nameof(RemoteInvoke)}: {fi.Name}")); + } + + return d.GetMethodInfo(); + } + } + + /// Options used with RemoteInvoke. + public sealed class RemoteInvokeOptions + { + public RemoteInvokeOptions(Dictionary environmentVariables = null) + { + if (environmentVariables != null) + { + foreach (var item in environmentVariables) + { + StartInfo.EnvironmentVariables.Add(item.Key, item.Value); + } + } + } + + public ProcessStartInfo StartInfo { get; set; } = new ProcessStartInfo(); + public bool CheckExitCode { get; set; } = true; + public int TimeOut { get; set; } = RemoteExecutor.FailWaitTimeoutMilliseconds; + public int ExpectedExitCode { get; set; } = RemoteExecutor.SuccessExitCode; + public string ExceptionFile { get; } = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + } +} diff --git a/test/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.cs b/test/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.cs new file mode 100644 index 0000000000..13adabd9f6 --- /dev/null +++ b/test/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.cs @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.ExceptionServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace RemoteExecutorConsoleApp +{ + class Program + { + static int Main(string[] args) + { + if (args.Length < 4) + { + Console.Error.WriteLine("Usage: {0} assemblyName typeName methodName exceptionFile [additionalArgs]", typeof(Program).GetTypeInfo().Assembly.GetName().Name); + Environment.Exit(-1); + return -1; + } + + string assemblyName = args[0]; + string typeName = args[1]; + string methodName = args[2]; + string exceptionFile = args[3]; + string[] additionalArgs = args.Length > 4 ? + Subarray(args, 4, args.Length - 4) : + Array.Empty(); + + // Load the specified assembly, type, and method, then invoke the method. + // The program's exit code is the return value of the invoked method. + Assembly a = null; + Type t = null; + MethodInfo mi = null; + object instance = null; + int exitCode = 0; + + try + { + // Create the test class if necessary + try + { + a = Assembly.Load(assemblyName); + } + catch(FileNotFoundException) + { + a = Assembly.LoadFrom(assemblyName.Split(',')[0] + ".dll"); + } + + t = a.GetType(typeName); + mi = t.GetTypeInfo().GetDeclaredMethod(methodName); + if (!mi.IsStatic) + { + instance = Activator.CreateInstance(t); + } + + // Invoke the test + object result = mi.Invoke(instance, additionalArgs); + + if (result is Task task) + { + exitCode = task.GetAwaiter().GetResult(); + } + else if (result is int exit) + { + exitCode = exit; + } + } + catch (Exception exc) + { + if (exc is TargetInvocationException && exc.InnerException != null) + exc = exc.InnerException; + + var output = new StringBuilder(); + output.AppendLine(); + output.AppendLine("Child exception:"); + output.AppendLine(" " + exc); + output.AppendLine(); + output.AppendLine("Child process:"); + output.AppendLine(string.Format(" {0} {1} {2}", a, t, mi)); + output.AppendLine(); + + if (additionalArgs.Length > 0) + { + output.AppendLine("Child arguments:"); + output.AppendLine(" " + string.Join(", ", additionalArgs)); + } + + File.WriteAllText(exceptionFile, output.ToString()); + + ExceptionDispatchInfo.Capture(exc).Throw(); + } + finally + { + (instance as IDisposable)?.Dispose(); + } + + // Use Exit rather than simply returning the exit code so that we forcibly shut down + // the process even if there are foreground threads created by the operation that would + // end up keeping the process alive potentially indefinitely. + try + { + Environment.Exit(exitCode); + } + catch (PlatformNotSupportedException) + { + } + + return exitCode; + } + + private static T[] Subarray(T[] arr, int offset, int count) + { + var newArr = new T[count]; + Array.Copy(arr, offset, newArr, 0, count); + return newArr; + } + } +} diff --git a/test/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.csproj b/test/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.csproj new file mode 100644 index 0000000000..4aee4bc458 --- /dev/null +++ b/test/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.csproj @@ -0,0 +1,7 @@ + + + + Exe + false + +