From ba15144658816a48b9e3cc02fad6209b3496a4e1 Mon Sep 17 00:00:00 2001 From: Joost van Zwieten Date: Tue, 11 Oct 2022 15:28:28 +0200 Subject: [PATCH 1/5] support multiple versions of MKL At the time of writing the MKL matrix backend the MKL libraries provided by Intel were unversioned (`libmkl_rt.so`, `libmkl_rt.dylib`, `mkl_rt.dll`). Since version 2021.1 the releases include libraries with major version 1 (`libmkl_rt.so.1`, `libmkl_rt.1.dylib`, `mkl_rt.1.dll`) in addition to the unversioned libraries. As of version 2021.2 the unversioned libraries are omitted. As of version 2022.0 the major library version has been incremented to 2. This patch adds support for the above mentioned versioned libraries. --- nutils/matrix/_mkl.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/nutils/matrix/_mkl.py b/nutils/matrix/_mkl.py index 6bd7d2c88..e9e306bc5 100644 --- a/nutils/matrix/_mkl.py +++ b/nutils/matrix/_mkl.py @@ -6,8 +6,11 @@ import os import numpy -libmkl = util.loadlib(linux='libmkl_rt.so', darwin='libmkl_rt.dylib', win32='mkl_rt.dll') -if not libmkl: +for v in '.2', '.1', '': + libmkl = util.loadlib(linux=f'libmkl_rt.so{v}', darwin=f'libmkl_rt{v}.dylib', win32=f'mkl_rt{v}.dll') + if libmkl: + break +else: raise BackendNotAvailable('the Intel MKL matrix backend requires libmkl to be installed (try: pip install mkl)') From a657b81354f0818802a11ba0214ab0c5c2551f1e Mon Sep 17 00:00:00 2001 From: Joost van Zwieten Date: Fri, 21 Oct 2022 11:03:20 +0200 Subject: [PATCH 2/5] add envvar NUTILS_MATRIX_MKL_LIB In GitHub Actions we've not been able to load MKL with `util.loadlib`, even when adding the path to `mkl_rt.?.dll` or `libmkl_rt.?.dylib` to `PATH` or `DYLD_LIBRARY_PATH`. To be able to test MKL and to aid users of the aformentioned platforms having the same problem, this patch adds the environment variable `NUTILS_MATRIX_MKL_LIB`, which should be a path to `mkl.?.dll` on Windows, `libmkl_rt.?.dylib` on MacOS and `libmkl.so.?` on Linux. If unempty, Nutils tries to load MKL from the provided path. If this fails, an execption will be raised. If the user provides a path to a different library, the behavior is undefined. --- nutils/matrix/_mkl.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/nutils/matrix/_mkl.py b/nutils/matrix/_mkl.py index e9e306bc5..45a2f2acf 100644 --- a/nutils/matrix/_mkl.py +++ b/nutils/matrix/_mkl.py @@ -1,17 +1,21 @@ from ._base import Matrix, MatrixError, BackendNotAvailable from .. import numeric, _util as util, warnings from contextlib import contextmanager -from ctypes import c_int, byref +from ctypes import c_int, byref, CDLL import treelog as log import os import numpy -for v in '.2', '.1', '': - libmkl = util.loadlib(linux=f'libmkl_rt.so{v}', darwin=f'libmkl_rt{v}.dylib', win32=f'mkl_rt{v}.dll') - if libmkl: - break +libmkl_path = os.environ.get('NUTILS_MATRIX_MKL_LIB', None) +if libmkl_path: + libmkl = CDLL(libmkl_path) else: - raise BackendNotAvailable('the Intel MKL matrix backend requires libmkl to be installed (try: pip install mkl)') + for v in '.2', '.1', '': + libmkl = util.loadlib(linux=f'libmkl_rt.so{v}', darwin=f'libmkl_rt{v}.dylib', win32=f'mkl_rt{v}.dll') + if libmkl: + break + else: + raise BackendNotAvailable('the Intel MKL matrix backend requires libmkl to be installed (try: pip install mkl)') def assemble(data, index, shape): From 164f8d3919de3f0b19f9614eb94808b9332c5041 Mon Sep 17 00:00:00 2001 From: Joost van Zwieten Date: Mon, 17 Oct 2022 13:35:00 +0200 Subject: [PATCH 3/5] disable symlinking libmkl in GitHub actions In a previous patch Nutils gained the ability to load versioned libraries of MKL, e.g. `libmkl.so.2` on Linux, in addition to unversioned libraries, e.g. `libmkl.so`. This patch removes the symlinking to `libmkl.so` in case only a versioned library was found in GitHub Actions --- devtools/gha/configure_mkl.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/devtools/gha/configure_mkl.py b/devtools/gha/configure_mkl.py index c31004540..9e0343f59 100644 --- a/devtools/gha/configure_mkl.py +++ b/devtools/gha/configure_mkl.py @@ -16,10 +16,6 @@ log.error('cannot find any of {}'.format(' '.join(candidates))) raise SystemExit(1) -lib = os.path.splitext(path)[0] -if not os.path.exists(lib): - os.symlink(path, lib) - ld_library_path = os.pathsep.join(filter(None, (os.environ.get('LD_LIBRARY_PATH', ''), os.path.dirname(path)))) with open(os.environ['GITHUB_ENV'], 'a') as f: print('LD_LIBRARY_PATH={}'.format(ld_library_path), file=f) From 6f3187dd8e29b22b09a2bd133d51fe04feac4bbc Mon Sep 17 00:00:00 2001 From: Joost van Zwieten Date: Mon, 17 Oct 2022 23:01:24 +0200 Subject: [PATCH 4/5] use pathlib.Path in devtools/gha/configure_mkl.py --- devtools/gha/configure_mkl.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/devtools/gha/configure_mkl.py b/devtools/gha/configure_mkl.py index 9e0343f59..f4aa3eeb4 100644 --- a/devtools/gha/configure_mkl.py +++ b/devtools/gha/configure_mkl.py @@ -2,20 +2,21 @@ import sys import site import os +from pathlib import Path libsubdir = 'lib' -prefixes = list(site.PREFIXES) +prefixes = list(map(Path, site.PREFIXES)) if hasattr(site, 'getuserbase'): - prefixes.append(site.getuserbase()) + prefixes.append(Path(site.getuserbase())) -candidates = [os.path.join(prefix, libsubdir, 'libmkl_rt.so' + ext) for prefix in prefixes for ext in ('.1', '.2')] +candidates = [prefix / libsubdir / f'libmkl_rt.so{ext}' for prefix in prefixes for ext in ('.1', '.2')] for path in candidates: - if os.path.exists(path): + if path.exists(): break else: - log.error('cannot find any of {}'.format(' '.join(candidates))) + log.error('cannot find any of {}'.format(' '.join(map(str, candidates)))) raise SystemExit(1) -ld_library_path = os.pathsep.join(filter(None, (os.environ.get('LD_LIBRARY_PATH', ''), os.path.dirname(path)))) +ld_library_path = os.pathsep.join(filter(None, (os.environ.get('LD_LIBRARY_PATH', ''), str(path.parent)))) with open(os.environ['GITHUB_ENV'], 'a') as f: print('LD_LIBRARY_PATH={}'.format(ld_library_path), file=f) From 2a66820fde62cd0b1966c55ec56df35118f54f37 Mon Sep 17 00:00:00 2001 From: Joost van Zwieten Date: Mon, 17 Oct 2022 23:15:23 +0200 Subject: [PATCH 5/5] test MKL on Windows and MacOS --- .github/workflows/test.yaml | 6 ++++-- devtools/gha/configure_mkl.py | 34 +++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 228dc0c1d..1ebc08d3e 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -49,8 +49,10 @@ jobs: - {name: "python 3.8", os: ubuntu-latest, python-version: "3.8", matrix-backend: numpy, nprocs: 1} - {name: "python 3.9", os: ubuntu-latest, python-version: "3.9", matrix-backend: numpy, nprocs: 1} - {name: "scipy matrix", os: ubuntu-latest, python-version: "3.10", matrix-backend: scipy, nprocs: 1} - - {name: "mkl matrix", os: ubuntu-latest, python-version: "3.10", matrix-backend: mkl, nprocs: 1} - - {name: "mkl matrix parallel", os: ubuntu-latest, python-version: "3.10", matrix-backend: mkl, nprocs: 2} + - {name: "mkl linux", os: ubuntu-latest, python-version: "3.10", matrix-backend: mkl, nprocs: 1} + - {name: "mkl linux parallel", os: ubuntu-latest, python-version: "3.10", matrix-backend: mkl, nprocs: 2} + - {name: "mkl windows", os: windows-latest, python-version: "3.10", matrix-backend: mkl, nprocs: 1} + - {name: "mkl macos", os: macos-latest, python-version: "3.10", matrix-backend: mkl, nprocs: 1} - {name: "parallel", os: ubuntu-latest, python-version: "3.10", matrix-backend: numpy, nprocs: 2} - {name: "numpy 1.17", os: ubuntu-latest, python-version: "3.7", matrix-backend: numpy, nprocs: 1, numpy-version: ==1.17} - {name: "tensorial", os: ubuntu-latest, python-version: "3.10", matrix-backend: numpy, nprocs: 1, tensorial: test} diff --git a/devtools/gha/configure_mkl.py b/devtools/gha/configure_mkl.py index f4aa3eeb4..05d3801e4 100644 --- a/devtools/gha/configure_mkl.py +++ b/devtools/gha/configure_mkl.py @@ -1,22 +1,38 @@ from .. import log +import re import sys import site import os from pathlib import Path -libsubdir = 'lib' +if sys.platform == 'linux': + libsubdir = 'lib', + re_libmkl = re.compile('libmkl_rt[.]so[.][0-9]+') +elif sys.platform == 'darwin': + libsubdir = 'lib', + re_libmkl = re.compile('libmkl_rt[.][0-9]+[.]dylib') +elif sys.platform == 'win32': + libsubdir = 'Library', 'bin' + re_libmkl = re.compile('mkl_rt[.][0-9]+[.]dll') +else: + log.error(f'unsupported platform: {sys.platform}') + raise SystemExit(1) + prefixes = list(map(Path, site.PREFIXES)) if hasattr(site, 'getuserbase'): prefixes.append(Path(site.getuserbase())) -candidates = [prefix / libsubdir / f'libmkl_rt.so{ext}' for prefix in prefixes for ext in ('.1', '.2')] -for path in candidates: - if path.exists(): - break -else: - log.error('cannot find any of {}'.format(' '.join(map(str, candidates)))) +libdirs = {libdir := prefix.joinpath(*libsubdir).resolve() for prefix in prefixes} +libs = {file for libdir in libdirs if libdir.is_dir() for file in libdir.iterdir() if re_libmkl.match(file.name)} +if len(libs) == 0: + log.error('cannot find MKL in any of {}'.format(', '.join(map(str, libdirs)))) + raise SystemExit(1) +elif len(libs) != 1: + log.error('found MKL at more than one location: {}'.format(', '.join(map(str, libs)))) raise SystemExit(1) +else: + lib, = libs + log.info(f'using MKL at {lib}') -ld_library_path = os.pathsep.join(filter(None, (os.environ.get('LD_LIBRARY_PATH', ''), str(path.parent)))) with open(os.environ['GITHUB_ENV'], 'a') as f: - print('LD_LIBRARY_PATH={}'.format(ld_library_path), file=f) + print(f'NUTILS_MATRIX_MKL_LIB={lib}', file=f)