Skip to content

Commit faff26c

Browse files
authored
Rework how local toolkits are selected. (#2058)
Split the single `version` preference, which could be set to `"local"`, into a separate `version` and `local` preference. As a result, even when using a local toolkit, CUDA.jl now knows at precompilation time which toolkit it's targeting, and can do conditional things like versioning the header wrappers.
1 parent a08b759 commit faff26c

File tree

14 files changed

+166
-133
lines changed

14 files changed

+166
-133
lines changed

.buildkite/pipeline.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ steps:
1818
queue: "juliagpu"
1919
cuda: "*"
2020
commands: |
21-
echo -e "[CUDA_Runtime_jll]\nversion = \"local\"" >LocalPreferences.toml
21+
echo -e "[CUDA_Runtime_jll]\nlocal = \"true\"" >LocalPreferences.toml
2222
if: build.message !~ /\[skip tests\]/ &&
2323
build.message !~ /\[skip julia\]/
2424
timeout_in_minutes: 120

Dockerfile

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,12 @@ ENV JULIA_DEPOT_PATH=/usr/local/share/julia
1010
RUN julia -e 'using Pkg; Pkg.add("CUDA")'
1111

1212
# hard-code a CUDA toolkit version
13-
RUN julia -e 'using CUDA; CUDA.set_runtime_version!(v"11.8")'
13+
RUN julia -e 'using CUDA; CUDA.set_runtime_version!(v"12.2")'
1414
# re-importing CUDA.jl below will trigger a download of the relevant artifacts
1515

1616
# generate the device runtime library for all known and supported devices.
1717
# this is to avoid having to do this over and over at run time.
18-
RUN julia -e 'using CUDA; CUDA.precompile_runtime()' && \
19-
chmod 644 /usr/local/share/julia/compiled/v1.8/GPUCompiler/*/*.bc
20-
# TODO: fix this in GPUCompiler.jl
18+
RUN julia -e 'using CUDA; CUDA.precompile_runtime()'
2119

2220

2321
# user environment

LocalPreferences.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
[CUDA_Runtime_jll]
2-
# which CUDA runtime to use; can be set to a supported
3-
# version number or to "local" for a local installation.
2+
# which CUDA runtime to use. in normal cases, this will be auto-detected, but you need to
3+
# set this preference if you want to precompile CUDA.jl in an envirnonment without CUDA
44
#version = "11.8"
55

6+
# whether to use a local CUDA installation. if CUDA isn't available during precompilation,
7+
# you will also need to set the "version" preference, matching the local CUDA version.
8+
#local = "false"
9+
610
[CUDA]
711
# whether to use a nonblocking synchronization mechanism,
812
# making it possible to do use cooperative multitasking.

Manifest.toml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
julia_version = "1.8.5"
44
manifest_format = "2.0"
5-
project_hash = "62b8680a215cbfae7c33bbe0a528c176464c0f65"
5+
project_hash = "e8fd324aba09b72a771344d3910c4e1284ae62a0"
66

77
[[deps.AbstractFFTs]]
88
deps = ["ChainRulesCore", "LinearAlgebra", "Test"]
@@ -57,9 +57,9 @@ version = "0.2.2"
5757

5858
[[deps.CUDA_Runtime_jll]]
5959
deps = ["Artifacts", "CUDA_Driver_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "TOML"]
60-
git-tree-sha1 = "2d1a9bacfcde420c3b3c16e8f72ee037bf35ba1e"
60+
git-tree-sha1 = "5b88716bfcb250bc036f9d4fa2915f72ef5bf17e"
6161
uuid = "76a88914-d11a-5bdc-97e0-2f5a05c973a2"
62-
version = "0.8.0+0"
62+
version = "0.9.0+0"
6363

6464
[[deps.ChainRulesCore]]
6565
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
@@ -272,18 +272,18 @@ uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
272272

273273
[[deps.LogExpFunctions]]
274274
deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"]
275-
git-tree-sha1 = "5ab83e1679320064c29e8973034357655743d22d"
275+
git-tree-sha1 = "7d6dd4e9212aebaeed356de34ccf262a3cd415aa"
276276
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
277-
version = "0.3.25"
277+
version = "0.3.26"
278278

279279
[[deps.Logging]]
280280
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
281281

282282
[[deps.MacroTools]]
283283
deps = ["Markdown", "Random"]
284-
git-tree-sha1 = "42324d08725e200c23d4dfb549e0d5d89dede2d2"
284+
git-tree-sha1 = "9ee1618cbf5240e6d4e0371d6f24065083f60c48"
285285
uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
286-
version = "0.5.10"
286+
version = "0.5.11"
287287

288288
[[deps.Markdown]]
289289
deps = ["Base64"]
@@ -360,9 +360,9 @@ version = "1.4.2"
360360

361361
[[deps.PrecompileTools]]
362362
deps = ["Preferences"]
363-
git-tree-sha1 = "9673d39decc5feece56ef3940e5dafba15ba0f81"
363+
git-tree-sha1 = "03b4c25b43cb84cee5c90aa9b5ea0a78fd848d2f"
364364
uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
365-
version = "1.1.2"
365+
version = "1.2.0"
366366

367367
[[deps.Preferences]]
368368
deps = ["TOML"]

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ BFloat16s = "0.2, 0.3, 0.4"
4242
CEnum = "0.2, 0.3, 0.4"
4343
CUDA_Driver_jll = "0.6"
4444
CUDA_Runtime_Discovery = "0.2"
45-
CUDA_Runtime_jll = "0.8"
45+
CUDA_Runtime_jll = "0.9"
4646
Crayons = "4"
4747
DataFrames = "1"
4848
ExprTools = "0.1"

docs/src/installation/overview.md

Lines changed: 30 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,7 @@ You can choose which version to (try to) download and use by calling
118118
julia> using CUDA
119119
120120
julia> CUDA.set_runtime_version!(v"11.8")
121-
┌ Warning: CUDA Runtime version set to 11.8, please re-start Julia for this to take effect.
122-
└ @ CUDA /usr/local/share/julia/packages/CUDA/irdEw/lib/cudadrv/version.jl:54
121+
[ Info: Set CUDA.jl toolkit preference to use CUDA 11.8.0 from artifact sources, please re-start Julia for this to take effect.
123122
```
124123

125124
This generates the following `LocalPreferences.toml` file in your active environment:
@@ -134,7 +133,8 @@ only select artifacts that are compatible with the configured CUDA runtime.
134133

135134
### Using a local CUDA
136135

137-
To use a local installation, you can invoke the same API but set the version to `"local"`:
136+
To use a local installation, you set the `local_toolkit` keyword argument to
137+
`CUDA.set_runtime_version!`:
138138

139139
```
140140
julia> using CUDA
@@ -143,9 +143,8 @@ julia> CUDA.versioninfo()
143143
CUDA runtime 11.8, artifact installation
144144
...
145145
146-
julia> CUDA.set_runtime_version!("local")
147-
┌ Warning: CUDA Runtime version set to local, please re-start Julia for this to take effect.
148-
└ @ CUDA ~/Julia/pkg/CUDA/lib/cudadrv/version.jl:73
146+
julia> CUDA.set_runtime_version!(local_toolkit=true)
147+
[ Info: Set CUDA.jl toolkit preference to use CUDA from the local system, please re-start Julia for this to take effect.
149148
```
150149

151150
After re-launching Julia:
@@ -163,7 +162,7 @@ your active environment:
163162

164163
```
165164
[CUDA_Runtime_jll]
166-
version = "local"
165+
local = "true"
167166
```
168167

169168
This preference not only configures CUDA.jl to use a local toolkit, it also prevents
@@ -174,53 +173,33 @@ If CUDA.jl doesn't properly detect your local toolkit, it may be that certain li
174173
binaries aren't on a globally-discoverable path. For more information, run Julia with the
175174
`JULIA_DEBUG` environment variable set to `CUDA_Runtime_Discovery`.
176175

177-
Note that setting the version to `"local"` disables use of *any* CUDA-related JLL, not just
178-
of `CUDA_Runtime_jll`. This is out of necessity: JLLs are baked in the precompilation image at
179-
compile time, while local toolkit discovery happens at run time; this inconsistency makes it
180-
impossible to select a compatible artifact for the JLLs. If you care about other JLLs, use
181-
CUDA from artifacts.
182-
183-
184-
## Containers
185-
186-
CUDA.jl is container friendly: You can install, precompile, and even import the package on a
187-
system without a GPU:
188-
189-
```
190-
$ docker run --rm -it julia # note how we're *not* using `--gpus=all` here,
191-
# so we won't have access to a GPU (or its driver)
192-
193-
pkg> add CUDA
194-
195-
pkg> precompile
196-
Precompiling project...
197-
[ Info: Precompiling CUDA [052768ef-5323-5732-b1bb-66c8b64840ba]
198-
```
176+
Note that using a local toolkit instead of artifacts *any* CUDA-related JLL, not just of
177+
`CUDA_Runtime_jll`. Any package that depends on such a JLL needs to inspect
178+
`CUDA.local_toolkit`, and if set use `CUDA_Runtime_Discovery` to detect libraries and
179+
binaries instead.
199180

200-
The above is common when building a container (`docker build` does not take a `--gpus`
201-
argument). It does prevent CUDA.jl from downloading the toolkit artifacts that will be
202-
required at run time, because it cannot query the driver for the CUDA compatibility level.
203181

204-
To avoid having to download the CUDA toolkit artifacts each time you restart your container,
205-
it's possible to inform CUDA.jl which toolkit to use. This can be done by calling
206-
`CUDA.set_runtime_version!` when building the container, after which a subsequent import
207-
of CUDA.jl will download the necessary artifacts.
182+
## Precompiling CUDA.jl without CUDA
208183

209-
At run time you obviously do need a CUDA-compatible GPU as well as the CUDA driver library
210-
to interface with it. Typically, that library is imported from the host system, e.g., by
211-
launching `docker` using the `--gpus=all` flag:
184+
CUDA.jl can be precompiled and imported on systems without a GPU or CUDA installation. This
185+
simplifies the situation where an application optionally uses CUDA. However, when CUDA.jl
186+
is precompiled in such an environment, it *cannot* be used to run GPU code. This is a
187+
result of artifacts being selected at precompile time.
212188

213-
```
214-
$ docker run --rm -it --gpus=all julia
189+
In some cases, e.g. with containers or HPC log-in nodes, you may want to precompile CUDA.jl
190+
on a system without CUDA, yet still want to have it download the necessary artifacts and/or
191+
produce a precompilation image that can be used on a system with CUDA. This can be achieved
192+
by informing CUDA.jl which CUDA toolkit to run time by calling `CUDA.set_runtime_version!`.
215193

216-
julia> using CUDA
217-
218-
julia> CUDA.versioninfo()
219-
CUDA runtime 11.8
220-
CUDA driver 11.8
221-
NVIDIA driver 520.56.6
222-
223-
...
224-
```
194+
When using artifacts, that's as simple as e.g. calling `CUDA.set_runtime_version!(v"11.8")`,
195+
and afterwards re-starting Julia and re-importing CUDA.jl in order to trigger precompilation
196+
again and download the necessary artifacts. If you want to use a local CUDA installation,
197+
you also need to set the `local_toolkit` keyword argument, e.g., by calling
198+
`CUDA.set_runtime_version!(v"11.8"; local_toolkit=true)`. Note that the version specified
199+
here needs to match what will be available at run time. In both cases, i.e. when using
200+
artifacts or a local toolkit, the chosen version needs to be compatible with the available
201+
driver.
225202

226-
All of the above is demonstrated in the Dockerfile that's part of the CUDA.jl repository.
203+
Finally, in such a scenario you may also want to call `CUDA.precompile_runtime()` to ensure
204+
that the GPUCompiler runtime library is precompiled as well. This and all of the above is
205+
demonstrated in the Dockerfile that's part of the CUDA.jl repository.

lib/cudadrv/version.jl

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,28 +43,45 @@ function runtime_version()
4343
end
4444

4545
"""
46-
set_runtime_version!([version])
46+
set_runtime_version!([version::VersionNumber]; local_toolkit=false)
4747
48-
Sets the CUDA Runtime version preference to `version`. This can be a version number, in
49-
which case such a versioned artifact will be attempted to be used; or "local" for using a
50-
runtime from the local system. Invoke this function without an argument to reset the
51-
preference, in which case CUDA.jl will use the most recent compatible runtime available.
48+
Configures CUDA.jl to use a specific CUDA toolkit version from a specific source.
49+
50+
If `local_toolkit` is set, the CUDA toolkit will be used from the local system, otherwise
51+
it will be downloaded from an artifact source. In the case of a local toolkit, `version`
52+
informs CUDA.jl which version that is (this may be useful if auto-detection fails). In
53+
the case of artifact sources, `version` controls which version will be downloaded and used.
54+
55+
See also: [`reset_runtime_version!`](@ref).
5256
"""
53-
function set_runtime_version!(version::String)
54-
Preferences.set_preferences!(CUDA_Runtime_jll, "version" => version; force=true)
55-
@info "Set CUDA Runtime version preference to $version, please re-start Julia for this to take effect."
56-
if VERSION <= v"1.6.5" || VERSION == v"1.7.0"
57-
@warn """Due to a bug in Julia (until 1.6.5 and 1.7.1) your environment needs to directly include CUDA_Runtime_jll for this to work."""
57+
function set_runtime_version!(version::Union{Nothing,VersionNumber}=nothing;
58+
local_toolkit::Bool=false)
59+
if version !== nothing
60+
Preferences.set_preferences!(CUDA_Runtime_jll, "version" => "$(version.major).$(version.minor)"; force=true)
61+
else
62+
Preferences.delete_preferences!(CUDA_Runtime_jll, "version"; force=true)
63+
end
64+
if local_toolkit
65+
Preferences.set_preferences!(CUDA_Runtime_jll, "local" => "true"; force=true)
66+
else
67+
# the default is "false"
68+
Preferences.delete_preferences!(CUDA_Runtime_jll, "local"; force=true)
5869
end
70+
@info "Set CUDA.jl toolkit preference to use $(version === nothing ? "CUDA" : "CUDA $version") from $(local_toolkit ? "the local system" : "artifact sources"), please re-start Julia for this to take effect."
5971
end
60-
set_runtime_version!(version::VersionNumber) =
61-
set_runtime_version!("$(version.major).$(version.minor)")
62-
function set_runtime_version!()
72+
73+
"""
74+
reset_runtime_version!()
75+
76+
Resets the CUDA Runtime version preference to the default, which is to use the most recent
77+
compatible runtime available from an artifact source.
78+
79+
See also: [`set_runtime_version!`](@ref).
80+
"""
81+
function reset_runtime_version!()
6382
Preferences.delete_preferences!(CUDA_Runtime_jll, "version"; force=true)
64-
@info "Reset CUDA Runtime version preference, please re-start Julia for this to take effect."
65-
if VERSION <= v"1.6.5" || VERSION == v"1.7.0"
66-
@warn """Due to a bug in Julia (until 1.6.5 and 1.7.1) your environment needs to directly include CUDA_Runtime_jll for this to work."""
67-
end
83+
Preferences.delete_preferences!(CUDA_Runtime_jll, "local"; force=true)
84+
@info "Reset CUDA.jl toolkit preference, please re-start Julia for this to take effect."
6885
end
6986

7087

lib/cudnn/src/cuDNN.jl

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ using CUDA
1111
using CUDA.APIUtils
1212
using CUDA: CUstream, libraryPropertyType
1313
using CUDA: retry_reclaim, isdebug, initialize_context
14-
using CUDA: CUDA_Runtime, CUDA_Runtime_jll
1514

1615
using CEnum: @cenum
1716

18-
import CUDNN_jll
17+
if CUDA.local_toolkit
18+
using CUDA_Runtime_Discovery
19+
else
20+
import CUDNN_jll
21+
end
1922

2023

2124
export has_cudnn
@@ -154,21 +157,22 @@ function __init__()
154157

155158
CUDA.functional() || return
156159

160+
# find the library
157161
global libcudnn
158-
if CUDA_Runtime == CUDA_Runtime_jll
159-
if !CUDNN_jll.is_available()
160-
precompiling || @error "cuDNN is not available for your platform ($(Base.BinaryPlatforms.triplet(CUDNN_jll.host_platform)))"
161-
return
162-
end
163-
libcudnn = CUDNN_jll.libcudnn
164-
else
165-
dirs = CUDA_Runtime.find_toolkit()
166-
path = CUDA_Runtime.get_library(dirs, "cudnn"; optional=true)
162+
if CUDA.local_toolkit
163+
dirs = CUDA_Runtime_Discovery.find_toolkit()
164+
path = CUDA_Runtime_Discovery.get_library(dirs, "cudnn"; optional=true)
167165
if path === nothing
168166
precompiling || @error "cuDNN is not available on your system (looked in $(join(dirs, ", ")))"
169167
return
170168
end
171169
libcudnn = path
170+
else
171+
if !CUDNN_jll.is_available()
172+
precompiling || @error "cuDNN is not available for your platform ($(Base.BinaryPlatforms.triplet(CUDNN_jll.host_platform)))"
173+
return
174+
end
175+
libcudnn = CUDNN_jll.libcudnn
172176
end
173177

174178
# register a log callback

lib/custatevec/src/cuStateVec.jl

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ module cuStateVec
33
using CUDA
44
using CUDA: CUstream, cudaDataType, @checked, HandleCache, with_workspace, libraryPropertyType
55
using CUDA: unsafe_free!, retry_reclaim, initialize_context, isdebug
6-
using CUDA: CUDA_Runtime, CUDA_Runtime_jll
76

87
using CEnum: @cenum
98

10-
import cuQuantum_jll
9+
if CUDA.local_toolkit
10+
using CUDA_Runtime_Discovery
11+
else
12+
import cuQuantum_jll
13+
end
1114

1215

1316
export has_custatevec
@@ -106,21 +109,22 @@ function __init__()
106109

107110
CUDA.functional() || return
108111

112+
# find the library
109113
global libcustatevec
110-
if CUDA_Runtime == CUDA_Runtime_jll
111-
if !cuQuantum_jll.is_available()
112-
precompiling || @error "cuQuantum is not available for your platform ($(Base.BinaryPlatforms.triplet(cuQuantum_jll.host_platform)))"
113-
return
114-
end
115-
libcustatevec = cuQuantum_jll.libcustatevec
116-
else
114+
if CUDA.local_toolkit
117115
dirs = CUDA_Runtime.find_toolkit()
118116
path = CUDA_Runtime.get_library(dirs, "custatevec"; optional=true)
119117
if path === nothing
120118
precompiling || @error "cuQuantum is not available on your system (looked for custatevec in $(join(dirs, ", ")))"
121119
return
122120
end
123121
libcustatevec = path
122+
else
123+
if !cuQuantum_jll.is_available()
124+
precompiling || @error "cuQuantum is not available for your platform ($(Base.BinaryPlatforms.triplet(cuQuantum_jll.host_platform)))"
125+
return
126+
end
127+
libcustatevec = cuQuantum_jll.libcustatevec
124128
end
125129

126130
# register a log callback

0 commit comments

Comments
 (0)