From d177c11972d4d3cc1cd1ec092a52d31b7f1e04bd Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Fri, 10 Dec 2021 23:17:34 +0100 Subject: [PATCH] Providers: attempt to use runtimes folders in provider packages (hybrid) --- build/MathNet.Numerics.CUDA.Win.nuspec | 16 ++- build/MathNet.Numerics.MKL.Linux-x64.nuspec | 12 +- build/MathNet.Numerics.MKL.Linux-x86.nuspec | 12 +- build/MathNet.Numerics.MKL.Linux.nuspec | 16 ++- build/MathNet.Numerics.MKL.Win-x64.nuspec | 12 +- build/MathNet.Numerics.MKL.Win-x86.nuspec | 12 +- build/MathNet.Numerics.MKL.Win.nuspec | 16 ++- build/MathNet.Numerics.OpenBLAS.Win.nuspec | 28 +++-- build/NativeProvider.targets | 6 +- src/Providers.CUDA/NativeProviderLoader.cs | 106 ++++++++++++----- src/Providers.MKL/NativeProviderLoader.cs | 108 +++++++++++++----- .../NativeProviderLoader.cs | 106 ++++++++++++----- 12 files changed, 284 insertions(+), 166 deletions(-) diff --git a/build/MathNet.Numerics.CUDA.Win.nuspec b/build/MathNet.Numerics.CUDA.Win.nuspec index 18c297df..928a1139 100644 --- a/build/MathNet.Numerics.CUDA.Win.nuspec +++ b/build/MathNet.Numerics.CUDA.Win.nuspec @@ -5,24 +5,22 @@ @build.number@ Math.NET Numerics - CUDA Native Provider for Windows (x64) Matthew A Johnson, Christoph Ruegg - Matthew A Johnson, Christoph Ruegg false - https://numerics.mathdotnet.com/License.html + MIT https://numerics.mathdotnet.com/ - https://www.mathdotnet.com/images/MathNet128.png Nvidia CUDA native libraries for Math.NET Numerics. Nvidia CUDA native libraries for Math.NET Numerics. @releaseNotes@ + Copyright Math.NET Project math numeric statistics probability integration interpolation linear algebra matrix fft native cuda gpu - - - - + + + + - - + diff --git a/build/MathNet.Numerics.MKL.Linux-x64.nuspec b/build/MathNet.Numerics.MKL.Linux-x64.nuspec index 84a95856..5d0ce96e 100644 --- a/build/MathNet.Numerics.MKL.Linux-x64.nuspec +++ b/build/MathNet.Numerics.MKL.Linux-x64.nuspec @@ -5,23 +5,21 @@ @build.number@ Math.NET Numerics - MKL Native Provider for Linux (x64) Christoph Ruegg, Marcus Cuda, Jurgen Van Gael - Christoph Ruegg, Marcus Cuda, Jurgen Van Gael false - https://numerics.mathdotnet.com/License.html + MIT https://numerics.mathdotnet.com/ - https://www.mathdotnet.com/images/MathNet128.png Intel MKL native libraries for Math.NET Numerics on Linux. Intel MKL native libraries for Math.NET Numerics on Linux. @releaseNotes@ + Copyright Math.NET Project math numeric statistics probability integration interpolation linear algebra matrix fft native mkl @dependencies@ - - + + - - + diff --git a/build/MathNet.Numerics.MKL.Linux-x86.nuspec b/build/MathNet.Numerics.MKL.Linux-x86.nuspec index 35125b42..266d5b3f 100644 --- a/build/MathNet.Numerics.MKL.Linux-x86.nuspec +++ b/build/MathNet.Numerics.MKL.Linux-x86.nuspec @@ -5,23 +5,21 @@ @build.number@ Math.NET Numerics - MKL Native Provider for Linux (x86) Christoph Ruegg, Marcus Cuda, Jurgen Van Gael - Christoph Ruegg, Marcus Cuda, Jurgen Van Gael false - https://numerics.mathdotnet.com/License.html + MIT https://numerics.mathdotnet.com/ - https://www.mathdotnet.com/images/MathNet128.png Intel MKL native libraries for Math.NET Numerics on Linux. Intel MKL native libraries for Math.NET Numerics on Linux. @releaseNotes@ + Copyright Math.NET Project math numeric statistics probability integration interpolation linear algebra matrix fft native mkl @dependencies@ - - + + - - + diff --git a/build/MathNet.Numerics.MKL.Linux.nuspec b/build/MathNet.Numerics.MKL.Linux.nuspec index 3d70dfd1..a7408557 100644 --- a/build/MathNet.Numerics.MKL.Linux.nuspec +++ b/build/MathNet.Numerics.MKL.Linux.nuspec @@ -5,25 +5,23 @@ @build.number@ Math.NET Numerics - MKL Native Provider for Linux (x64 and x86) Christoph Ruegg, Marcus Cuda, Jurgen Van Gael - Christoph Ruegg, Marcus Cuda, Jurgen Van Gael false - https://numerics.mathdotnet.com/License.html + MIT https://numerics.mathdotnet.com/ - https://www.mathdotnet.com/images/MathNet128.png Intel MKL native libraries for Math.NET Numerics on Linux. Intel MKL native libraries for Math.NET Numerics on Linux. @releaseNotes@ + Copyright Math.NET Project math numeric statistics probability integration interpolation linear algebra matrix fft native mkl @dependencies@ - - - - + + + + - - + diff --git a/build/MathNet.Numerics.MKL.Win-x64.nuspec b/build/MathNet.Numerics.MKL.Win-x64.nuspec index 6e51aa84..5940436e 100644 --- a/build/MathNet.Numerics.MKL.Win-x64.nuspec +++ b/build/MathNet.Numerics.MKL.Win-x64.nuspec @@ -5,23 +5,21 @@ @build.number@ Math.NET Numerics - MKL Native Provider for Windows (x64) Christoph Ruegg, Marcus Cuda, Jurgen Van Gael - Christoph Ruegg, Marcus Cuda, Jurgen Van Gael false - https://numerics.mathdotnet.com/License.html + MIT https://numerics.mathdotnet.com/ - https://www.mathdotnet.com/images/MathNet128.png Intel oneAPI MKL native libraries for Math.NET Numerics on Windows. Intel oneAPI MKL native libraries for Math.NET Numerics on Windows. @releaseNotes@ + Copyright Math.NET Project math numeric statistics probability integration interpolation linear algebra matrix fft native mkl @dependencies@ - - + + - - + diff --git a/build/MathNet.Numerics.MKL.Win-x86.nuspec b/build/MathNet.Numerics.MKL.Win-x86.nuspec index 86158e0f..29215dfa 100644 --- a/build/MathNet.Numerics.MKL.Win-x86.nuspec +++ b/build/MathNet.Numerics.MKL.Win-x86.nuspec @@ -5,23 +5,21 @@ @build.number@ Math.NET Numerics - MKL Native Provider for Windows (x86) Christoph Ruegg, Marcus Cuda, Jurgen Van Gael - Christoph Ruegg, Marcus Cuda, Jurgen Van Gael false - https://numerics.mathdotnet.com/License.html + MIT https://numerics.mathdotnet.com/ - https://www.mathdotnet.com/images/MathNet128.png Intel oneAPI MKL native libraries for Math.NET Numerics on Windows. Intel oneAPI MKL native libraries for Math.NET Numerics on Windows. @releaseNotes@ + Copyright Math.NET Project math numeric statistics probability integration interpolation linear algebra matrix fft native mkl @dependencies@ - - + + - - + diff --git a/build/MathNet.Numerics.MKL.Win.nuspec b/build/MathNet.Numerics.MKL.Win.nuspec index b2c9f3e7..227c9a76 100644 --- a/build/MathNet.Numerics.MKL.Win.nuspec +++ b/build/MathNet.Numerics.MKL.Win.nuspec @@ -5,25 +5,23 @@ @build.number@ Math.NET Numerics - MKL Native Provider for Windows (x64 and x86) Christoph Ruegg, Marcus Cuda, Jurgen Van Gael - Christoph Ruegg, Marcus Cuda, Jurgen Van Gael false - https://numerics.mathdotnet.com/License.html + MIT https://numerics.mathdotnet.com/ - https://www.mathdotnet.com/images/MathNet128.png Intel oneAPI MKL native libraries for Math.NET Numerics on Windows. Intel oneAPI MKL native libraries for Math.NET Numerics on Windows. @releaseNotes@ + Copyright Math.NET Project math numeric statistics probability integration interpolation linear algebra matrix fft native mkl @dependencies@ - - - - + + + + - - + diff --git a/build/MathNet.Numerics.OpenBLAS.Win.nuspec b/build/MathNet.Numerics.OpenBLAS.Win.nuspec index 26d225e3..f64dc155 100644 --- a/build/MathNet.Numerics.OpenBLAS.Win.nuspec +++ b/build/MathNet.Numerics.OpenBLAS.Win.nuspec @@ -5,31 +5,29 @@ @build.number@ Math.NET Numerics - OpenBLAS Native Provider for Windows (x64 and x86) Kuan Bartel, Christoph Ruegg, Marcus Cuda - Kuan Bartel, Christoph Ruegg, Marcus Cuda false - https://numerics.mathdotnet.com/License.html + MIT https://numerics.mathdotnet.com/ - https://www.mathdotnet.com/images/MathNet128.png OpenBLAS native libraries for Math.NET Numerics. OpenBLAS native libraries for Math.NET Numerics. @releaseNotes@ + Copyright Math.NET Project math numeric statistics probability integration interpolation linear algebra matrix fft native openblas @dependencies@ - - - - - - - - - - + + + + + + + + + + - - + diff --git a/build/NativeProvider.targets b/build/NativeProvider.targets index d32847dc..805634c5 100644 --- a/build/NativeProvider.targets +++ b/build/NativeProvider.targets @@ -4,9 +4,9 @@ ** Math.NET Native Provider Interop Files ** ****************************************************************************** --> - - - + + + %(RecursiveDir)%(FileName)%(Extension) PreserveNewest diff --git a/src/Providers.CUDA/NativeProviderLoader.cs b/src/Providers.CUDA/NativeProviderLoader.cs index 75a923b4..b1e72510 100644 --- a/src/Providers.CUDA/NativeProviderLoader.cs +++ b/src/Providers.CUDA/NativeProviderLoader.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2016 Math.NET +// Copyright (c) 2009-2021 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -37,6 +37,17 @@ using System.Threading; namespace MathNet.Numerics.Providers.CUDA { + internal enum Runtime + { + Unknown = 0, + WindowsX64, + WindowsX86, + WindowsArm64, + WindowsArm, + LinuxX64, + LinuxX86, + } + /// /// Helper class to load native libraries depending on the architecture of the OS and process. /// @@ -44,12 +55,6 @@ namespace MathNet.Numerics.Providers.CUDA { static readonly object StaticLock = new Object(); - const string X86 = "x86"; - const string X64 = "x64"; - const string IA64 = "ia64"; - const string ARM = "arm"; - const string ARM64 = "arm64"; - /// /// Dictionary of handles to previously loaded libraries, /// @@ -58,7 +63,7 @@ namespace MathNet.Numerics.Providers.CUDA /// /// Gets a string indicating the architecture and bitness of the current process. /// - static readonly Lazy ArchitectureKey = new Lazy(EvaluateArchitectureKey, LazyThreadSafetyMode.PublicationOnly); + static readonly Lazy RuntimeKey = new Lazy(EvaluateRuntime, LazyThreadSafetyMode.PublicationOnly); /// /// If the last native library failed to load then gets the corresponding exception @@ -75,42 +80,35 @@ namespace MathNet.Numerics.Providers.CUDA } } - static string EvaluateArchitectureKey() + static Runtime EvaluateRuntime() { //return (IntPtr.Size == 8) ? X64 : X86; if (IsUnix) { - // Only support x86 and amd64 on Unix as there isn't a reliable way to detect the architecture - return Environment.Is64BitProcess ? X64 : X86; - + return Environment.Is64BitProcess ? Runtime.LinuxX64 : Runtime.LinuxX86; } var architecture = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"); if (string.Equals(architecture, "x86", StringComparison.OrdinalIgnoreCase)) { - return X86; + return Runtime.WindowsX86; } if (string.Equals(architecture, "amd64", StringComparison.OrdinalIgnoreCase) || string.Equals(architecture, "x64", StringComparison.OrdinalIgnoreCase)) { - return Environment.Is64BitProcess ? X64 : X86; - } - - if (string.Equals(architecture, "ia64", StringComparison.OrdinalIgnoreCase)) - { - return IA64; + return Environment.Is64BitProcess ? Runtime.WindowsX64 : Runtime.WindowsX86; } if (string.Equals(architecture, "arm", StringComparison.OrdinalIgnoreCase)) { - return Environment.Is64BitProcess ? ARM64 : ARM; + return Environment.Is64BitProcess ? Runtime.WindowsArm64 : Runtime.WindowsArm; } // Fallback if unknown - return architecture; + return Runtime.Unknown; } /// @@ -169,31 +167,77 @@ namespace MathNet.Numerics.Providers.CUDA directory = Path.GetFullPath(directory); // If we have a know architecture, try the matching subdirectory first - var architecture = ArchitectureKey.Value; - if (!string.IsNullOrEmpty(architecture) && TryLoadFile(new FileInfo(Path.Combine(Path.Combine(directory, architecture), fileName)))) + switch (RuntimeKey.Value) { - return true; + case Runtime.WindowsX64: + if (TryLoadFile(directory, "x64", fileName) + || TryLoadFile(directory, "win-x64/native", fileName) + || TryLoadFile(directory, "win-x64", fileName)) + { + return true; + } + break; + case Runtime.WindowsX86: + if (TryLoadFile(directory, "x86", fileName) + || TryLoadFile(directory, "win-x86/native", fileName) + || TryLoadFile(directory, "win-x86", fileName)) + { + return true; + } + break; + case Runtime.WindowsArm64: + if (TryLoadFile(directory, "arm64", fileName) + || TryLoadFile(directory, "win-arm64/native", fileName) + || TryLoadFile(directory, "win-arm64", fileName)) + { + return true; + } + break; + case Runtime.WindowsArm: + if (TryLoadFile(directory, "arm", fileName) + || TryLoadFile(directory, "win-arm/native", fileName) + || TryLoadFile(directory, "win-arm", fileName)) + { + return true; + } + break; + case Runtime.LinuxX64: + if (TryLoadFile(directory, "x64", fileName) + || TryLoadFile(directory, "linux-x64/native", fileName) + || TryLoadFile(directory, "linux-x64", fileName)) + { + return true; + } + break; + case Runtime.LinuxX86: + if (TryLoadFile(directory, "x86", fileName) + || TryLoadFile(directory, "linux-x86/native", fileName) + || TryLoadFile(directory, "linux-x86", fileName)) + { + return true; + } + break; } // Otherwise try to load directly from the provided directory - return TryLoadFile(new FileInfo(Path.Combine(directory, fileName))); + return TryLoadFile(directory, string.Empty, fileName); } /// /// Try to load a native library by providing the full path including the file name of the library. /// /// True if the library was successfully loaded or if it has already been loaded. - static bool TryLoadFile(FileInfo file) + static bool TryLoadFile(string directory, string relativePath, string fileName) { lock (StaticLock) { - IntPtr libraryHandle; - if (NativeHandles.Value.TryGetValue(file.Name, out libraryHandle)) + if (NativeHandles.Value.TryGetValue(fileName, out IntPtr libraryHandle)) { return true; } - if (!file.Exists) + var fullPath = Path.GetFullPath(Path.Combine(Path.Combine(directory, relativePath), fileName)); + if (!File.Exists(fullPath)) { // If the library isn't found within an architecture specific folder then return false // to allow normal P/Invoke searching behavior when the library is called @@ -201,7 +245,7 @@ namespace MathNet.Numerics.Providers.CUDA } // If successful this will return a handle to the library - libraryHandle = IsUnix ? UnixLoader.LoadLibrary(file.FullName) : WindowsLoader.LoadLibrary(file.FullName); + libraryHandle = IsUnix ? UnixLoader.LoadLibrary(fullPath) : WindowsLoader.LoadLibrary(fullPath); if (libraryHandle == IntPtr.Zero) { int lastError = Marshal.GetLastWin32Error(); @@ -211,7 +255,7 @@ namespace MathNet.Numerics.Providers.CUDA else { LastException = null; - NativeHandles.Value[file.Name] = libraryHandle; + NativeHandles.Value[fileName] = libraryHandle; } return libraryHandle != IntPtr.Zero; diff --git a/src/Providers.MKL/NativeProviderLoader.cs b/src/Providers.MKL/NativeProviderLoader.cs index 96d21224..9eea4b23 100644 --- a/src/Providers.MKL/NativeProviderLoader.cs +++ b/src/Providers.MKL/NativeProviderLoader.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2016 Math.NET +// Copyright (c) 2009-2021 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -35,8 +35,21 @@ using System.Runtime.InteropServices; using System.Security; using System.Threading; +// ReSharper disable InconsistentNaming + namespace MathNet.Numerics.Providers.MKL { + internal enum Runtime + { + Unknown = 0, + WindowsX64, + WindowsX86, + WindowsArm64, + WindowsArm, + LinuxX64, + LinuxX86, + } + /// /// Helper class to load native libraries depending on the architecture of the OS and process. /// @@ -44,12 +57,6 @@ namespace MathNet.Numerics.Providers.MKL { static readonly object StaticLock = new Object(); - const string X86 = "x86"; - const string X64 = "x64"; - const string IA64 = "ia64"; - const string ARM = "arm"; - const string ARM64 = "arm64"; - /// /// Dictionary of handles to previously loaded libraries, /// @@ -58,7 +65,7 @@ namespace MathNet.Numerics.Providers.MKL /// /// Gets a string indicating the architecture and bitness of the current process. /// - static readonly Lazy ArchitectureKey = new Lazy(EvaluateArchitectureKey, LazyThreadSafetyMode.PublicationOnly); + static readonly Lazy RuntimeKey = new Lazy(EvaluateRuntime, LazyThreadSafetyMode.PublicationOnly); /// /// If the last native library failed to load then gets the corresponding exception @@ -75,42 +82,35 @@ namespace MathNet.Numerics.Providers.MKL } } - static string EvaluateArchitectureKey() + static Runtime EvaluateRuntime() { //return (IntPtr.Size == 8) ? X64 : X86; if (IsUnix) { - // Only support x86 and amd64 on Unix as there isn't a reliable way to detect the architecture - return Environment.Is64BitProcess ? X64 : X86; - + return Environment.Is64BitProcess ? Runtime.LinuxX64 : Runtime.LinuxX86; } var architecture = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"); if (string.Equals(architecture, "x86", StringComparison.OrdinalIgnoreCase)) { - return X86; + return Runtime.WindowsX86; } if (string.Equals(architecture, "amd64", StringComparison.OrdinalIgnoreCase) || string.Equals(architecture, "x64", StringComparison.OrdinalIgnoreCase)) { - return Environment.Is64BitProcess ? X64 : X86; - } - - if (string.Equals(architecture, "ia64", StringComparison.OrdinalIgnoreCase)) - { - return IA64; + return Environment.Is64BitProcess ? Runtime.WindowsX64 : Runtime.WindowsX86; } if (string.Equals(architecture, "arm", StringComparison.OrdinalIgnoreCase)) { - return Environment.Is64BitProcess ? ARM64 : ARM; + return Environment.Is64BitProcess ? Runtime.WindowsArm64 : Runtime.WindowsArm; } // Fallback if unknown - return architecture; + return Runtime.Unknown; } /// @@ -169,31 +169,77 @@ namespace MathNet.Numerics.Providers.MKL directory = Path.GetFullPath(directory); // If we have a know architecture, try the matching subdirectory first - var architecture = ArchitectureKey.Value; - if (!string.IsNullOrEmpty(architecture) && TryLoadFile(new FileInfo(Path.Combine(Path.Combine(directory, architecture), fileName)))) + switch (RuntimeKey.Value) { - return true; + case Runtime.WindowsX64: + if (TryLoadFile(directory, "x64", fileName) + || TryLoadFile(directory, "win-x64/native", fileName) + || TryLoadFile(directory, "win-x64", fileName)) + { + return true; + } + break; + case Runtime.WindowsX86: + if (TryLoadFile(directory, "x86", fileName) + || TryLoadFile(directory, "win-x86/native", fileName) + || TryLoadFile(directory, "win-x86", fileName)) + { + return true; + } + break; + case Runtime.WindowsArm64: + if (TryLoadFile(directory, "arm64", fileName) + || TryLoadFile(directory, "win-arm64/native", fileName) + || TryLoadFile(directory, "win-arm64", fileName)) + { + return true; + } + break; + case Runtime.WindowsArm: + if (TryLoadFile(directory, "arm", fileName) + || TryLoadFile(directory, "win-arm/native", fileName) + || TryLoadFile(directory, "win-arm", fileName)) + { + return true; + } + break; + case Runtime.LinuxX64: + if (TryLoadFile(directory, "x64", fileName) + || TryLoadFile(directory, "linux-x64/native", fileName) + || TryLoadFile(directory, "linux-x64", fileName)) + { + return true; + } + break; + case Runtime.LinuxX86: + if (TryLoadFile(directory, "x86", fileName) + || TryLoadFile(directory, "linux-x86/native", fileName) + || TryLoadFile(directory, "linux-x86", fileName)) + { + return true; + } + break; } // Otherwise try to load directly from the provided directory - return TryLoadFile(new FileInfo(Path.Combine(directory, fileName))); + return TryLoadFile(directory, string.Empty, fileName); } /// /// Try to load a native library by providing the full path including the file name of the library. /// /// True if the library was successfully loaded or if it has already been loaded. - static bool TryLoadFile(FileInfo file) + static bool TryLoadFile(string directory, string relativePath, string fileName) { lock (StaticLock) { - IntPtr libraryHandle; - if (NativeHandles.Value.TryGetValue(file.Name, out libraryHandle)) + if (NativeHandles.Value.TryGetValue(fileName, out IntPtr libraryHandle)) { return true; } - if (!file.Exists) + var fullPath = Path.GetFullPath(Path.Combine(Path.Combine(directory, relativePath), fileName)); + if (!File.Exists(fullPath)) { // If the library isn't found within an architecture specific folder then return false // to allow normal P/Invoke searching behavior when the library is called @@ -201,7 +247,7 @@ namespace MathNet.Numerics.Providers.MKL } // If successful this will return a handle to the library - libraryHandle = IsUnix ? UnixLoader.LoadLibrary(file.FullName) : WindowsLoader.LoadLibrary(file.FullName); + libraryHandle = IsUnix ? UnixLoader.LoadLibrary(fullPath) : WindowsLoader.LoadLibrary(fullPath); if (libraryHandle == IntPtr.Zero) { int lastError = Marshal.GetLastWin32Error(); @@ -211,7 +257,7 @@ namespace MathNet.Numerics.Providers.MKL else { LastException = null; - NativeHandles.Value[file.Name] = libraryHandle; + NativeHandles.Value[fileName] = libraryHandle; } return libraryHandle != IntPtr.Zero; diff --git a/src/Providers.OpenBLAS/NativeProviderLoader.cs b/src/Providers.OpenBLAS/NativeProviderLoader.cs index 17518d53..c8b995cf 100644 --- a/src/Providers.OpenBLAS/NativeProviderLoader.cs +++ b/src/Providers.OpenBLAS/NativeProviderLoader.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2016 Math.NET +// Copyright (c) 2009-2021 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -37,6 +37,17 @@ using System.Threading; namespace MathNet.Numerics.Providers.OpenBLAS { + internal enum Runtime + { + Unknown = 0, + WindowsX64, + WindowsX86, + WindowsArm64, + WindowsArm, + LinuxX64, + LinuxX86, + } + /// /// Helper class to load native libraries depending on the architecture of the OS and process. /// @@ -44,12 +55,6 @@ namespace MathNet.Numerics.Providers.OpenBLAS { static readonly object StaticLock = new Object(); - const string X86 = "x86"; - const string X64 = "x64"; - const string IA64 = "ia64"; - const string ARM = "arm"; - const string ARM64 = "arm64"; - /// /// Dictionary of handles to previously loaded libraries, /// @@ -58,7 +63,7 @@ namespace MathNet.Numerics.Providers.OpenBLAS /// /// Gets a string indicating the architecture and bitness of the current process. /// - static readonly Lazy ArchitectureKey = new Lazy(EvaluateArchitectureKey, LazyThreadSafetyMode.PublicationOnly); + static readonly Lazy RuntimeKey = new Lazy(EvaluateRuntime, LazyThreadSafetyMode.PublicationOnly); /// /// If the last native library failed to load then gets the corresponding exception @@ -75,42 +80,35 @@ namespace MathNet.Numerics.Providers.OpenBLAS } } - static string EvaluateArchitectureKey() + static Runtime EvaluateRuntime() { //return (IntPtr.Size == 8) ? X64 : X86; if (IsUnix) { - // Only support x86 and amd64 on Unix as there isn't a reliable way to detect the architecture - return Environment.Is64BitProcess ? X64 : X86; - + return Environment.Is64BitProcess ? Runtime.LinuxX64 : Runtime.LinuxX86; } var architecture = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"); if (string.Equals(architecture, "x86", StringComparison.OrdinalIgnoreCase)) { - return X86; + return Runtime.WindowsX86; } if (string.Equals(architecture, "amd64", StringComparison.OrdinalIgnoreCase) || string.Equals(architecture, "x64", StringComparison.OrdinalIgnoreCase)) { - return Environment.Is64BitProcess ? X64 : X86; - } - - if (string.Equals(architecture, "ia64", StringComparison.OrdinalIgnoreCase)) - { - return IA64; + return Environment.Is64BitProcess ? Runtime.WindowsX64 : Runtime.WindowsX86; } if (string.Equals(architecture, "arm", StringComparison.OrdinalIgnoreCase)) { - return Environment.Is64BitProcess ? ARM64 : ARM; + return Environment.Is64BitProcess ? Runtime.WindowsArm64 : Runtime.WindowsArm; } // Fallback if unknown - return architecture; + return Runtime.Unknown; } /// @@ -169,31 +167,77 @@ namespace MathNet.Numerics.Providers.OpenBLAS directory = Path.GetFullPath(directory); // If we have a know architecture, try the matching subdirectory first - var architecture = ArchitectureKey.Value; - if (!string.IsNullOrEmpty(architecture) && TryLoadFile(new FileInfo(Path.Combine(Path.Combine(directory, architecture), fileName)))) + switch (RuntimeKey.Value) { - return true; + case Runtime.WindowsX64: + if (TryLoadFile(directory, "x64", fileName) + || TryLoadFile(directory, "win-x64/native", fileName) + || TryLoadFile(directory, "win-x64", fileName)) + { + return true; + } + break; + case Runtime.WindowsX86: + if (TryLoadFile(directory, "x86", fileName) + || TryLoadFile(directory, "win-x86/native", fileName) + || TryLoadFile(directory, "win-x86", fileName)) + { + return true; + } + break; + case Runtime.WindowsArm64: + if (TryLoadFile(directory, "arm64", fileName) + || TryLoadFile(directory, "win-arm64/native", fileName) + || TryLoadFile(directory, "win-arm64", fileName)) + { + return true; + } + break; + case Runtime.WindowsArm: + if (TryLoadFile(directory, "arm", fileName) + || TryLoadFile(directory, "win-arm/native", fileName) + || TryLoadFile(directory, "win-arm", fileName)) + { + return true; + } + break; + case Runtime.LinuxX64: + if (TryLoadFile(directory, "x64", fileName) + || TryLoadFile(directory, "linux-x64/native", fileName) + || TryLoadFile(directory, "linux-x64", fileName)) + { + return true; + } + break; + case Runtime.LinuxX86: + if (TryLoadFile(directory, "x86", fileName) + || TryLoadFile(directory, "linux-x86/native", fileName) + || TryLoadFile(directory, "linux-x86", fileName)) + { + return true; + } + break; } // Otherwise try to load directly from the provided directory - return TryLoadFile(new FileInfo(Path.Combine(directory, fileName))); + return TryLoadFile(directory, string.Empty, fileName); } /// /// Try to load a native library by providing the full path including the file name of the library. /// /// True if the library was successfully loaded or if it has already been loaded. - static bool TryLoadFile(FileInfo file) + static bool TryLoadFile(string directory, string relativePath, string fileName) { lock (StaticLock) { - IntPtr libraryHandle; - if (NativeHandles.Value.TryGetValue(file.Name, out libraryHandle)) + if (NativeHandles.Value.TryGetValue(fileName, out IntPtr libraryHandle)) { return true; } - if (!file.Exists) + var fullPath = Path.GetFullPath(Path.Combine(Path.Combine(directory, relativePath), fileName)); + if (!File.Exists(fullPath)) { // If the library isn't found within an architecture specific folder then return false // to allow normal P/Invoke searching behavior when the library is called @@ -201,7 +245,7 @@ namespace MathNet.Numerics.Providers.OpenBLAS } // If successful this will return a handle to the library - libraryHandle = IsUnix ? UnixLoader.LoadLibrary(file.FullName) : WindowsLoader.LoadLibrary(file.FullName); + libraryHandle = IsUnix ? UnixLoader.LoadLibrary(fullPath) : WindowsLoader.LoadLibrary(fullPath); if (libraryHandle == IntPtr.Zero) { int lastError = Marshal.GetLastWin32Error(); @@ -211,7 +255,7 @@ namespace MathNet.Numerics.Providers.OpenBLAS else { LastException = null; - NativeHandles.Value[file.Name] = libraryHandle; + NativeHandles.Value[fileName] = libraryHandle; } return libraryHandle != IntPtr.Zero;