Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mkl_path preference to support using "system" MKL #84

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MKL_jll = "856f044c-d86e-5d09-b602-aeab76dc8ba7"
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
carstenbauer marked this conversation as resolved.
Show resolved Hide resolved

[compat]
MKL_jll = "2022.1"
julia = "1.8"
Preferences = "1.4"

[extras]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Expand Down
27 changes: 20 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,23 @@

MKL.jl is a Julia package that allows users to use the Intel MKL library for Julia's underlying BLAS and LAPACK, instead of OpenBLAS, which Julia ships with by default. Julia includes [libblastrampoline](https://github.com/staticfloat/libblastrampoline), which enables picking a BLAS and LAPACK library at runtime. A [JuliaCon 2021 talk](https://www.youtube.com/watch?v=t6hptekOR7s) provides details on this mechanism.

This package requires Julia 1.7+
This package requires Julia 1.7+.

## Usage

If you want to use `MKL.jl` in your project, make sure it is the first package you load before any other package. It is essential that MKL be loaded before other packages so that it can find the Intel OMP library and avoid [issues resulting out of GNU OMP being loaded first](https://github.com/JuliaPackaging/BinaryBuilder.jl/issues/700).
## Installation

## To Install:
To install the package execute

Adding the package will replace the system BLAS and LAPACK with MKL provided ones at runtime. Note that the MKL package has to be loaded in every new Julia process. Upon quitting and restarting, Julia will start with the default OpenBLAS.
```julia
julia> using Pkg; Pkg.add("MKL")
```

## To Check Installation:
## Usage

Loading the package (`using MKL`) will replace the system BLAS and LAPACK with MKL provided ones at runtime. Make sure it is the first package you load before any other package. It is essential that MKL be loaded before other packages so that it can find the Intel OMP library and avoid [issues resulting out of GNU OMP being loaded first](https://github.com/JuliaPackaging/BinaryBuilder.jl/issues/700).

Note that the MKL package has to be loaded in every new Julia process. Upon quitting and restarting, Julia will start with the default OpenBLAS.

## Check

```julia
julia> using LinearAlgebra
Expand All @@ -35,6 +38,16 @@ Libraries:
└ [ILP64] libmkl_rt.1.dylib
```

Note that you can put `using MKL` into your `startup.jl` to make Julia automatically use Intel MKL in every session.

## Using the 64-bit vs 32-bit version of MKL

We use ILP64 by default on 64-bit systems, and LP64 on 32-bit systems.

## Using a system-provided Intel MKL

If you want to use a system-provided Intel MKL installation, you can set the [preference](https://github.com/JuliaPackaging/Preferences.jl) `mkl_path` to hint MKL.jl to the corresponding `libmkl_rt` library. Specifically, the options are:

* `mkl_jll` (default): Download and install MKL via [MKL_jll.jl](https://github.com/JuliaBinaryWrappers/MKL_jll.jl).
* `system`: The package will try to automatically locate the system-provided libmkl_rt library (i.e. find it on the linker search path).
* `path/to/my/libmkl_rt.<EXT>`: Explicit path to the `libmkl_rt.<EXT>` where `<EXT>` is the shared library extension of the system at hand (e.g. `.so`, `.dll`, `.dylib`)
86 changes: 64 additions & 22 deletions src/MKL.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,36 @@
module MKL

using MKL_jll

using Preferences
using Libdl
using LinearAlgebra

# Choose an MKL path; taking an explicit preference as the first choice,
# but if nothing is set as a preference, fall back to the default choice of `MKL_jll`.
const mkl_path = something(
@load_preference("mkl_path", nothing),
"mkl_jll",
)::String

if lowercase(mkl_path) == "mkl_jll"
# Only load MKL_jll if we are suppoed to use it as the MKL source
# to avoid an unnecessary download of the (lazy) artifact.
import MKL_jll
const mkl_found = MKL_jll.is_available()
const libmkl_rt = mkl_found ? MKL_jll.libmkl_rt : nothing
elseif lowercase(mkl_path) == "system"
# We expect the "system" MKL to already be loaded,
# or be on our linker search path.
libname = string("libmkl_rt", ".", Libdl.dlext)
const libmkl_rt = find_library(libname, [""])
const mkl_found = libmkl_rt != ""
mkl_found || @warn("Couldn't find $libname. Try to specify the path to `libmkl_rt` explicitly.")
else
# mkl_path should be a valid path to libmkl_rt.
const libmkl_rt = mkl_path
const mkl_found = isfile(libmkl_rt)
mkl_found || @warn("Couldn't find MKL library at $libmkl_rt.")
end

if Base.USE_BLAS64
const MKLBlasInt = Int64
else
Expand All @@ -24,6 +51,14 @@ end
INTERFACE_GNU
end

function set_mkl_path(path)
if lowercase(path) ∉ ("mkl_jll", "system") && !isfile(path)
error("The provided argument $path neither seems to be a valid path to libmkl_rt nor \"mkl_jll\" or \"system\".")
end
@set_preferences!("mkl_path" => path)
@info("New MKL preference set; please restart Julia to see this take effect", path)
end

function set_threading_layer(layer::Threading = THREADING_SEQUENTIAL)
err = ccall((:MKL_Set_Threading_Layer, libmkl_rt), Cint, (Cint,), layer)
err == -1 && throw(ErrorException("MKL_Set_Threading_Layer() returned -1"))
Expand All @@ -36,29 +71,36 @@ function set_interface_layer(interface::Interface = INTERFACE_LP64)
return nothing
end

function __init__()
if MKL_jll.is_available()
if Sys.isapple()
set_threading_layer(THREADING_SEQUENTIAL)
end
# MKL 2022 and onwards have 64_ for ILP64 suffixes. The LP64 interface
# includes LP64 APIs for the non-suffixed symbols and ILP64 API for the
# 64_ suffixed symbols. LBT4 in Julia is necessary for this to work.
set_interface_layer(INTERFACE_LP64)
if Base.USE_BLAS64
# Load ILP64 forwards
BLAS.lbt_forward(libmkl_rt; clear=true, suffix_hint="64")
# Load LP64 forward
BLAS.lbt_forward(libmkl_rt; suffix_hint="")
else
BLAS.lbt_forward(libmkl_rt; clear=true, suffix_hint="")
end
end
end

function mklnorm(x::Vector{Float64})
ccall((:dnrm2_, libmkl_rt), Float64,
(Ref{MKLBlasInt}, Ptr{Float64}, Ref{MKLBlasInt}), length(x), x, 1)
end

function lbt_mkl_forwarding()
if Sys.isapple()
set_threading_layer(THREADING_SEQUENTIAL)
end
# MKL 2022 and onwards have 64_ for ILP64 suffixes. The LP64 interface
# includes LP64 APIs for the non-suffixed symbols and ILP64 API for the
# 64_ suffixed symbols. LBT4 in Julia is necessary for this to work.
set_interface_layer(INTERFACE_LP64)
if Base.USE_BLAS64
# Load ILP64 forwards
BLAS.lbt_forward(libmkl_rt; clear=true, suffix_hint="64")
# Load LP64 forward
BLAS.lbt_forward(libmkl_rt; suffix_hint="")
else
BLAS.lbt_forward(libmkl_rt; clear=true, suffix_hint="")
end
return nothing
end

function __init__()
if mkl_found
lbt_mkl_forwarding()
else
@warn("MKL library couldn't be found. Please make sure to set the `mkl_path` preference correctly (e.g. via `MKL.set_mkl_path`).")
end
end

end # module