From ad05896d3b4e317c3e5418e5fadf3968604fdbcd Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 9 Aug 2020 13:40:18 +0200 Subject: [PATCH] AppContext app switches to disable native providers or their probing --- MathNet.Numerics.sln.DotSettings | 1 + src/Numerics/AppSwitches.cs | 109 ++++++++++++++++++ src/Numerics/Control.cs | 20 +++- .../Providers/Common/Cuda/CudaProvider.cs | 12 +- .../Providers/Common/Mkl/MklProvider.cs | 12 +- .../Common/OpenBlas/OpenBlasProvider.cs | 12 +- .../FourierTransformControl.cs | 19 ++- .../LinearAlgebra/LinearAlgebraControl.cs | 21 +++- .../Mkl/MklSparseSolverProvider.cs | 4 +- .../SparseSolver/SparseSolverControl.cs | 48 +++++++- 10 files changed, 248 insertions(+), 10 deletions(-) create mode 100644 src/Numerics/AppSwitches.cs diff --git a/MathNet.Numerics.sln.DotSettings b/MathNet.Numerics.sln.DotSettings index 5eeaa191..61294516 100644 --- a/MathNet.Numerics.sln.DotSettings +++ b/MathNet.Numerics.sln.DotSettings @@ -1,6 +1,7 @@  <?xml version="1.0" encoding="utf-16"?><Profile name="Full Cleanup (Math.NET)"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSRemoveCodeRedundancies>True</CSRemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUpdateFileHeader>True</CSUpdateFileHeader><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><XMLReformatCode>True</XMLReformatCode><CssAlphabetizeProperties>True</CssAlphabetizeProperties><CssReformatCode>True</CssReformatCode><JsReformatCode>True</JsReformatCode><JsInsertSemicolon>True</JsInsertSemicolon><VBFormatDocComments>True</VBFormatDocComments><VBReformatCode>True</VBReformatCode><VBShortenReferences>True</VBShortenReferences><VBOptimizeImports>True</VBOptimizeImports><HtmlReformatCode>True</HtmlReformatCode><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><CSReorderTypeMembers>True</CSReorderTypeMembers><CSUseVar><BehavourStyle>CAN_CHANGE_BOTH</BehavourStyle><LocalVariableStyle>IMPLICIT_WHEN_INITIALIZER_HAS_TYPE</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar></Profile> Full Cleanup (Math.NET) + Implicit False False False diff --git a/src/Numerics/AppSwitches.cs b/src/Numerics/AppSwitches.cs new file mode 100644 index 00000000..ce8f9246 --- /dev/null +++ b/src/Numerics/AppSwitches.cs @@ -0,0 +1,109 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// https://numerics.mathdotnet.com +// https://github.com/mathnet/mathnet-numerics +// https://mathnetnumerics.codeplex.com +// +// Copyright (c) 2009-2020 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +namespace MathNet.Numerics +{ + /// + /// AppContext based switches to disable functionality, controllable through also in the + /// host application through AppContext or by configuration with AppContextSwitchOverride. + /// https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/appcontextswitchoverrides-element + /// + /// + /// Since AppContext is not supported on .NET Framework 4.0, a local implementation is used there instead, + /// which cannot be controlled though configuration or through AppContext. + /// + public static class AppSwitches + { + const string AppSwitchDisableNativeProviderProbing = "Switch.MathNet.Numerics.Providers.DisableNativeProviderProbing"; + const string AppSwitchDisableNativeProviders = "Switch.MathNet.Numerics.Providers.DisableNativeProviders"; + const string AppSwitchDisableMklNativeProvider = "Switch.MathNet.Numerics.Providers.DisableMklNativeProvider"; + const string AppSwitchDisableAcmlNativeProvider = "Switch.MathNet.Numerics.Providers.DisableAcmlNativeProvider"; + const string AppSwitchDisableCudaNativeProvider = "Switch.MathNet.Numerics.Providers.DisableCudaNativeProvider"; + const string AppSwitchDisableOpenBlasNativeProvider = "Switch.MathNet.Numerics.Providers.DisableOpenBlasNativeProvider"; + +#if NET40 + static readonly System.Collections.Generic.Dictionary Switches = new System.Collections.Generic.Dictionary(); +#endif + + static void SetSwitch(string switchName, bool isEnabled) + { +#if NET40 + Switches[switchName] = isEnabled; +#else + System.AppContext.SetSwitch(switchName, isEnabled); +#endif + } + + static bool IsEnabled(string switchName) + { +#if NET40 + return Switches.TryGetValue(switchName, out bool isEnabled) && isEnabled; +#else + return System.AppContext.TryGetSwitch(switchName, out bool isEnabled) && isEnabled; +#endif + } + + public static bool DisableNativeProviderProbing + { + get => IsEnabled(AppSwitchDisableNativeProviderProbing); + set => SetSwitch(AppSwitchDisableNativeProviderProbing, value); + } + + public static bool DisableNativeProviders + { + get => IsEnabled(AppSwitchDisableNativeProviders); + set => SetSwitch(AppSwitchDisableNativeProviders, value); + } + + public static bool DisableMklNativeProvider + { + get => IsEnabled(AppSwitchDisableMklNativeProvider); + set => SetSwitch(AppSwitchDisableMklNativeProvider, value); + } + + public static bool DisableAcmlNativeProvider + { + get => IsEnabled(AppSwitchDisableAcmlNativeProvider); + set => SetSwitch(AppSwitchDisableAcmlNativeProvider, value); + } + + public static bool DisableCudaNativeProvider + { + get => IsEnabled(AppSwitchDisableCudaNativeProvider); + set => SetSwitch(AppSwitchDisableCudaNativeProvider, value); + } + + public static bool DisableOpenBlasNativeProvider + { + get => IsEnabled(AppSwitchDisableOpenBlasNativeProvider); + set => SetSwitch(AppSwitchDisableOpenBlasNativeProvider, value); + } + } +} diff --git a/src/Numerics/Control.cs b/src/Numerics/Control.cs index 9c8f1b35..38ce3010 100644 --- a/src/Numerics/Control.cs +++ b/src/Numerics/Control.cs @@ -87,6 +87,12 @@ namespace MathNet.Numerics /// public static void UseDefaultProviders() { + if (AppSwitches.DisableNativeProviders) + { + UseManaged(); + return; + } + LinearAlgebraControl.UseDefault(); FourierTransformControl.UseDefault(); SparseSolverControl.UseDefault(); @@ -97,6 +103,12 @@ namespace MathNet.Numerics /// public static void UseBestProviders() { + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableNativeProviderProbing) + { + UseManaged(); + return; + } + LinearAlgebraControl.UseBest(); FourierTransformControl.UseBest(); SparseSolverControl.UseBest(); @@ -198,9 +210,15 @@ namespace MathNet.Numerics /// public static bool TryUseNative() { + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableNativeProviderProbing) + { + return false; + } + bool linearAlgebra = LinearAlgebraControl.TryUseNative(); bool fourierTransform = FourierTransformControl.TryUseNative(); - return linearAlgebra || fourierTransform; + bool directSparseSolver = SparseSolverControl.TryUseNative(); + return linearAlgebra || fourierTransform || directSparseSolver; } #endif diff --git a/src/Numerics/Providers/Common/Cuda/CudaProvider.cs b/src/Numerics/Providers/Common/Cuda/CudaProvider.cs index d3494846..fc33ce60 100644 --- a/src/Numerics/Providers/Common/Cuda/CudaProvider.cs +++ b/src/Numerics/Providers/Common/Cuda/CudaProvider.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2018 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -52,6 +52,11 @@ namespace MathNet.Numerics.Providers.Common.Cuda return true; } + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableCudaNativeProvider) + { + return false; + } + try { if (!NativeProviderLoader.TryLoad(SafeNativeMethods.DllName, hintPath)) @@ -78,6 +83,11 @@ namespace MathNet.Numerics.Providers.Common.Cuda return _nativeRevision; } + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableCudaNativeProvider) + { + throw new NotSupportedException("CUDA Native Provider support is actively disabled by AppSwitches."); + } + int a, b; try { diff --git a/src/Numerics/Providers/Common/Mkl/MklProvider.cs b/src/Numerics/Providers/Common/Mkl/MklProvider.cs index 55364626..e0a73908 100644 --- a/src/Numerics/Providers/Common/Mkl/MklProvider.cs +++ b/src/Numerics/Providers/Common/Mkl/MklProvider.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2018 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -53,6 +53,11 @@ namespace MathNet.Numerics.Providers.Common.Mkl return true; } + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableMklNativeProvider) + { + return false; + } + try { if (!NativeProviderLoader.TryLoad(SafeNativeMethods.DllName, hintPath)) @@ -90,6 +95,11 @@ namespace MathNet.Numerics.Providers.Common.Mkl return _nativeRevision; } + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableMklNativeProvider) + { + throw new NotSupportedException("MKL Native Provider support is actively disabled by AppSwitches."); + } + int a, b; try { diff --git a/src/Numerics/Providers/Common/OpenBlas/OpenBlasProvider.cs b/src/Numerics/Providers/Common/OpenBlas/OpenBlasProvider.cs index b6fa1d94..34dc4127 100644 --- a/src/Numerics/Providers/Common/OpenBlas/OpenBlasProvider.cs +++ b/src/Numerics/Providers/Common/OpenBlas/OpenBlasProvider.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2018 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -53,6 +53,11 @@ namespace MathNet.Numerics.Providers.Common.OpenBlas return true; } + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableOpenBlasNativeProvider) + { + return false; + } + try { if (!NativeProviderLoader.TryLoad(SafeNativeMethods.DllName, hintPath)) @@ -79,6 +84,11 @@ namespace MathNet.Numerics.Providers.Common.OpenBlas return _nativeRevision; } + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableOpenBlasNativeProvider) + { + throw new NotSupportedException("OpenBLAS Native Provider support is actively disabled by AppSwitches."); + } + int a, b; try { diff --git a/src/Numerics/Providers/FourierTransform/FourierTransformControl.cs b/src/Numerics/Providers/FourierTransform/FourierTransformControl.cs index f5d2bdf7..ae755db7 100644 --- a/src/Numerics/Providers/FourierTransform/FourierTransformControl.cs +++ b/src/Numerics/Providers/FourierTransform/FourierTransformControl.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2018 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -107,6 +107,11 @@ namespace MathNet.Numerics.Providers.FourierTransform /// public static bool TryUseNative() { + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableNativeProviderProbing) + { + return false; + } + return TryUseNativeMKL(); } #endif @@ -135,6 +140,12 @@ namespace MathNet.Numerics.Providers.FourierTransform /// public static void UseBest() { + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableNativeProviderProbing) + { + UseManaged(); + return; + } + #if NATIVE if (!TryUseNative()) { @@ -152,6 +163,12 @@ namespace MathNet.Numerics.Providers.FourierTransform /// public static void UseDefault() { + if (AppSwitches.DisableNativeProviders) + { + UseManaged(); + return; + } + #if NATIVE var value = Environment.GetEnvironmentVariable(EnvVarFFTProvider); switch (value != null ? value.ToUpperInvariant() : string.Empty) diff --git a/src/Numerics/Providers/LinearAlgebra/LinearAlgebraControl.cs b/src/Numerics/Providers/LinearAlgebra/LinearAlgebraControl.cs index 174b5832..e2335e77 100644 --- a/src/Numerics/Providers/LinearAlgebra/LinearAlgebraControl.cs +++ b/src/Numerics/Providers/LinearAlgebra/LinearAlgebraControl.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2018 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -160,7 +160,12 @@ namespace MathNet.Numerics.Providers.LinearAlgebra /// public static bool TryUseNative() { - return TryUseNativeCUDA() || TryUseNativeMKL() || TryUseNativeOpenBLAS(); + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableNativeProviderProbing) + { + return false; + } + + return TryUseNativeMKL() || TryUseNativeOpenBLAS() || TryUseNativeCUDA(); } #endif @@ -188,6 +193,12 @@ namespace MathNet.Numerics.Providers.LinearAlgebra /// public static void UseBest() { + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableNativeProviderProbing) + { + UseManaged(); + return; + } + #if NATIVE if (!TryUseNative()) { @@ -205,6 +216,12 @@ namespace MathNet.Numerics.Providers.LinearAlgebra /// public static void UseDefault() { + if (AppSwitches.DisableNativeProviders) + { + UseManaged(); + return; + } + #if NATIVE var value = Environment.GetEnvironmentVariable(EnvVarLAProvider); switch (value != null ? value.ToUpperInvariant() : string.Empty) diff --git a/src/Numerics/Providers/SparseSolver/Mkl/MklSparseSolverProvider.cs b/src/Numerics/Providers/SparseSolver/Mkl/MklSparseSolverProvider.cs index e544cde7..84e92b5a 100644 --- a/src/Numerics/Providers/SparseSolver/Mkl/MklSparseSolverProvider.cs +++ b/src/Numerics/Providers/SparseSolver/Mkl/MklSparseSolverProvider.cs @@ -10,7 +10,7 @@ namespace MathNet.Numerics.Providers.SparseSolver.Mkl /// internal partial class MklSparseSolverProvider : Managed.ManagedSparseSolverProvider, IDisposable { - const int MinimumCompatibleRevision = 12; + const int MinimumCompatibleRevision = 14; readonly string _hintPath; @@ -43,7 +43,7 @@ namespace MathNet.Numerics.Providers.SparseSolver.Mkl { throw new NotSupportedException($"MKL Native Provider revision r{revision} is too old. Consider upgrading to a newer version. Revision r{MinimumCompatibleRevision} and newer are supported."); } - + sparseSolverMajor = SafeNativeMethods.query_capability((int)ProviderCapability.SparseSolverMajor); sparseSolverMinor = SafeNativeMethods.query_capability((int)ProviderCapability.SparseSolverMinor); if (!(sparseSolverMajor == 1 && sparseSolverMinor >= 0)) diff --git a/src/Numerics/Providers/SparseSolver/SparseSolverControl.cs b/src/Numerics/Providers/SparseSolver/SparseSolverControl.cs index 4f3cea0d..1c6b0fdb 100644 --- a/src/Numerics/Providers/SparseSolver/SparseSolverControl.cs +++ b/src/Numerics/Providers/SparseSolver/SparseSolverControl.cs @@ -1,4 +1,33 @@ -using System; +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// +// Copyright (c) 2009-2020 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +using System; namespace MathNet.Numerics.Providers.SparseSolver { @@ -78,6 +107,11 @@ namespace MathNet.Numerics.Providers.SparseSolver /// public static bool TryUseNative() { + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableNativeProviderProbing) + { + return false; + } + return TryUseNativeMKL(); } #endif @@ -106,6 +140,12 @@ namespace MathNet.Numerics.Providers.SparseSolver /// public static void UseBest() { + if (AppSwitches.DisableNativeProviders || AppSwitches.DisableNativeProviderProbing) + { + UseManaged(); + return; + } + #if NATIVE if (!TryUseNative()) { @@ -123,6 +163,12 @@ namespace MathNet.Numerics.Providers.SparseSolver /// public static void UseDefault() { + if (AppSwitches.DisableNativeProviders) + { + UseManaged(); + return; + } + #if NATIVE var value = Environment.GetEnvironmentVariable(EnvVarSSProvider); switch (value != null ? value.ToUpperInvariant() : string.Empty)