-
Notifications
You must be signed in to change notification settings - Fork 218
/
bindeps.jl
360 lines (299 loc) · 11.8 KB
/
bindeps.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# discovering binary CUDA dependencies
using Pkg, Pkg.Artifacts
import Libdl
## global state
const __toolkit_origin = Ref{Symbol}()
"""
toolkit_origin()
Returns the origin of the CUDA toolkit in use (either :artifact, or :local).
"""
toolkit_origin() = @after_init(__toolkit_origin[])
const __toolkit_version = Ref{VersionNumber}()
"""
toolkit_version()
Returns the version of the CUDA toolkit in use.
"""
toolkit_version() = @after_init(__toolkit_version[])
"""
toolkit_release()
Returns the CUDA release part of the version as returned by [`version`](@ref).
"""
toolkit_release() = @after_init(VersionNumber(__toolkit_version[].major, __toolkit_version[].minor))
const __nvdisasm = Ref{String}()
const __libdevice = Ref{String}()
const __libcudadevrt = Ref{String}()
const __libcupti = Ref{Union{Nothing,String}}()
const __libnvtx = Ref{Union{Nothing,String}}()
const __libcublas = Ref{String}()
const __libcusparse = Ref{String}()
const __libcusolver = Ref{String}()
const __libcufft = Ref{String}()
const __libcurand = Ref{String}()
const __libcudnn = Ref{Union{Nothing,String}}(nothing)
const __libcutensor = Ref{Union{Nothing,String}}(nothing)
nvdisasm() = @after_init(__nvdisasm[])
libdevice() = @after_init(__libdevice[])
libcudadevrt() = @after_init(__libcudadevrt[])
function libcupti()
@after_init begin
@assert has_cupti() "This functionality is unavailable as CUPTI is missing."
__libcupti[]
end
end
function libnvtx()
@after_init begin
@assert has_nvtx() "This functionality is unavailable as NVTX is missing."
__libnvtx[]
end
end
export has_cupti, has_nvtx
has_cupti() = @after_init(__libcupti[]) !== nothing
has_nvtx() = @after_init(__libnvtx[]) !== nothing
libcublas() = @after_init(__libcublas[])
libcusparse() = @after_init(__libcusparse[])
libcusolver() = @after_init(__libcusolver[])
libcufft() = @after_init(__libcufft[])
libcurand() = @after_init(__libcurand[])
function libcudnn()
@after_init begin
@assert has_cudnn() "This functionality is unavailabe as CUDNN is missing."
__libcudnn[]
end
end
function libcutensor()
@after_init begin
@assert has_cutensor() "This functionality is unavailabe as CUTENSOR is missing."
__libcutensor[]
end
end
export has_cudnn, has_cutensor
has_cudnn() = @after_init(__libcudnn[]) !== nothing
has_cutensor() = @after_init(__libcutensor[]) !== nothing
## discovery
# utilities to look up stuff in the artifact (at known locations)
artifact_binary(artifact_dir, name) = joinpath(artifact_dir, "bin", Sys.iswindows() ? "$name.exe" : name)
artifact_static_library(artifact_dir, name) = joinpath(artifact_dir, "lib", Sys.iswindows() ? "$name.lib" : "lib$name.a")
artifact_file(artifact_dir, path) = joinpath(artifact_dir, path)
function artifact_library(artifact, name, version)
dir = joinpath(artifact, Sys.iswindows() ? "bin" : "lib")
all_names = library_versioned_names(name, version)
for name in all_names
path = joinpath(dir, name)
ispath(path) && return path
end
error("Could not find $name ($(join(all_names, ", ", " or "))) in $dir")
end
function artifact_cuda_library(artifact, library, toolkit_version)
version = cuda_library_version(library, toolkit_version)
name = get(cuda_library_names, library, library)
artifact_library(artifact, name, version)
end
# CUDA
# workaround @artifact_str eagerness on unsupported platforms by passing a variable
lazy_artifact(x) = @artifact_str(x)
# NOTE: we don't use autogenerated JLLs, because we have multiple artifacts and need to
# decide at run time (i.e. not via package dependencies) which one to use.
const cuda_artifacts = Dict(
(release=v"11.1", version=v"11.1.0", preferred=true) => ()->lazy_artifact("CUDA111"),
(release=v"11.0", version=v"11.0.3", preferred=true) => ()->lazy_artifact("CUDA110"),
(release=v"10.2", version=v"10.2.89", preferred=true) => ()->lazy_artifact("CUDA102"),
(release=v"10.1", version=v"10.1.243", preferred=true) => ()->lazy_artifact("CUDA101"),
)
function use_artifact_cuda()
@debug "Trying to use artifacts..."
# select compatible artifacts
if haskey(ENV, "JULIA_CUDA_VERSION")
wanted = VersionNumber(ENV["JULIA_CUDA_VERSION"])
@debug "Selecting artifacts based on requested $wanted"
candidate_artifacts = filter(cuda_artifacts) do (cuda, artifact)
cuda.release == wanted || cuda.version == wanted
end
isempty(candidate_artifacts) && @debug "Requested CUDA version $wanted is not provided by any artifact"
else
driver_release = release()
@debug "Selecting artifacts based on driver compatibility $driver_release"
candidate_artifacts = filter(cuda_artifacts) do (cuda, artifact)
cuda.preferred && cuda.release <= driver_release
end
isempty(candidate_artifacts) && @debug "CUDA driver compatibility $driver_release is not compatible with any artifact"
end
# download and install
artifact = nothing
for cuda in sort(collect(keys(candidate_artifacts)); rev=true)
try
artifact = (version=cuda.version, release=cuda.release,
dir=candidate_artifacts[cuda]())
break
catch ex
@debug "Could not load the CUDA $(cuda.release) artifact" exception=(ex,catch_backtrace())
end
end
if artifact == nothing
@debug "Could not find a compatible artifact."
return false
end
__nvdisasm[] = artifact_binary(artifact.dir, "nvdisasm")
@assert isfile(__nvdisasm[])
__toolkit_version[] = parse_toolkit_version("nvdisasm", __nvdisasm[])
__libcupti[] = artifact_cuda_library(artifact.dir, "cupti", artifact.version)
@assert isfile(__libcupti[])
__libnvtx[] = artifact_cuda_library(artifact.dir, "nvtx", artifact.version)
@assert isfile(__libnvtx[])
__libcudadevrt[] = artifact_static_library(artifact.dir, "cudadevrt")
@assert isfile(__libcudadevrt[])
__libdevice[] = artifact_file(artifact.dir, joinpath("share", "libdevice", "libdevice.10.bc"))
@assert isfile(__libdevice[])
for library in ("cublas", "cusparse", "cusolver", "cufft", "curand")
handle = getfield(CUDA, Symbol("__lib$library"))
handle[] = artifact_cuda_library(artifact.dir, library, artifact.version)
Libdl.dlopen(handle[])
end
@debug "Using CUDA $(__toolkit_version[]) from an artifact at $(artifact.dir)"
__toolkit_origin[] = :artifact
use_artifact_cudnn(artifact.release)
use_artifact_cutensor(artifact.release)
return true
end
function use_local_cuda()
@debug "Trying to use local installation..."
cuda_dirs = find_toolkit()
let path = find_cuda_binary("nvdisasm", cuda_dirs)
if path === nothing
@debug "Could not find nvdisasm"
return false
end
__nvdisasm[] = path
end
cuda_version = parse_toolkit_version("nvdisasm", __nvdisasm[])
__toolkit_version[] = cuda_version
__libcupti[] = find_cuda_library("cupti", cuda_dirs, cuda_version)
__libnvtx[] = find_cuda_library("nvtx", cuda_dirs, cuda_version)
let path = find_libcudadevrt(cuda_dirs)
if path === nothing
@debug "Could not find libcudadevrt"
return false
end
__libcudadevrt[] = path
end
let path = find_libdevice(cuda_dirs)
if path === nothing
@debug "Could not find libdevice"
return false
end
__libdevice[] = path
end
for library in ("cublas", "cusparse", "cusolver", "cufft", "curand")
handle = getfield(CUDA, Symbol("__lib$library"))
path = find_cuda_library(library, cuda_dirs, cuda_version)
if path === nothing
@debug "Could not find $library"
return false
end
handle[] = path
end
@debug "Found local CUDA $(cuda_version) at $(join(cuda_dirs, ", "))"
__toolkit_origin[] = :local
use_local_cudnn(cuda_dirs)
use_local_cutensor(cuda_dirs)
return true
end
# CUDNN
const cudnn_artifacts = Dict(
v"11.1" => ()->(lazy_artifact("CUDNN_CUDA111"), v"8"),
v"11.0" => ()->(lazy_artifact("CUDNN_CUDA110"), v"8"),
v"10.2" => ()->(lazy_artifact("CUDNN_CUDA102"), v"8"),
v"10.1" => ()->(lazy_artifact("CUDNN_CUDA101"), v"8"),
)
function use_artifact_cudnn(release)
artifact_dir, version = try
cudnn_artifacts[release]()
catch ex
@debug "Could not use CUDNN from artifacts" exception=(ex, catch_backtrace())
return false
end
path = artifact_library(artifact_dir, "cudnn", version)
# HACK: eagerly open CUDNN sublibraries to avoid dlopen discoverability issues
for sublibrary in ("ops_infer", "ops_train",
"cnn_infer", "cnn_train",
"adv_infer", "adv_train")
sublibrary_path = artifact_library(artifact_dir, "cudnn_$(sublibrary)", version)
Libdl.dlopen(sublibrary_path)
end
Libdl.dlopen(path)
__libcudnn[] = path
@debug "Using CUDNN from an artifact at $(artifact_dir)"
return true
end
function use_local_cudnn(cuda_dirs)
path = find_library("cudnn", v"8"; locations=cuda_dirs)
path === nothing && return false
# HACK: eagerly open CUDNN sublibraries to avoid dlopen discoverability issues
for sublibrary in ("ops_infer", "ops_train",
"cnn_infer", "cnn_train",
"adv_infer", "adv_train")
sublibrary_path = find_library("cudnn_$(sublibrary)", v"8"; locations=cuda_dirs)
@assert sublibrary_path !== nothing "Could not find CUDNN sublibrary $sublibrary"
Libdl.dlopen(sublibrary_path)
end
Libdl.dlopen(path)
__libcudnn[] = path
@debug "Using local CUDNN at $(path)"
return true
end
# CUTENSOR
const cutensor_artifacts = Dict(
v"11.1" => ()->lazy_artifact("CUTENSOR_CUDA111"),
v"11.0" => ()->lazy_artifact("CUTENSOR_CUDA110"),
v"10.2" => ()->lazy_artifact("CUTENSOR_CUDA102"),
v"10.1" => ()->lazy_artifact("CUTENSOR_CUDA101"),
)
function use_artifact_cutensor(release)
artifact_dir = try
cutensor_artifacts[release]()
catch ex
@debug "Could not use CUTENSOR from artifacts" exception=(ex, catch_backtrace())
return false
end
version = Sys.iswindows() ? nothing : v"1" # cutensor.dll is unversioned on Windows
path = artifact_library(artifact_dir, "cutensor", version)
try
Libdl.dlopen(path)
catch ex
@error "Could not load CUTENSOR; please file an issue (if on Windows, be sure to install the VS C++ redistributable first)" exception=(ex,catch_backtrace())
return false
end
__libcutensor[] = path
@debug "Using CUTENSOR from an artifact at $(artifact_dir)"
return true
end
function use_local_cutensor(cuda_dirs)
path = find_library("cutensor", v"1"; locations=cuda_dirs)
if path === nothing
path = find_library("cutensor"; locations=cuda_dirs)
end
path === nothing && return false
try
Libdl.dlopen(path)
catch ex
@error "Could not load CUTENSOR; please file an issue (if on Windows, be sure to install the VS C++ redistributable first)" exception=(ex,catch_backtrace())
return false
end
__libcutensor[] = path
@debug "Using local CUTENSOR at $(path)"
return true
end
function __init_dependencies__()
found = false
# CI runs in a well-defined environment, so prefer a local CUDA installation there
if parse(Bool, get(ENV, "CI", "false")) && !haskey(ENV, "JULIA_CUDA_USE_BINARYBUILDER")
found = use_local_cuda()
end
if !found && parse(Bool, get(ENV, "JULIA_CUDA_USE_BINARYBUILDER", "true"))
found = use_artifact_cuda()
end
# if the user didn't specifically request an artifact version, look for a local installation
if !found && !haskey(ENV, "JULIA_CUDA_VERSION")
found = use_local_cuda()
end
return found
end