Skip to content

Commit 397825d

Browse files
committed
Add disk cache infrastructure for Julia 1.11
1 parent 46edf96 commit 397825d

File tree

6 files changed

+116
-5
lines changed

6 files changed

+116
-5
lines changed

Project.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
99
LLVM = "929cbde3-209d-540e-8aea-75f648917ca0"
1010
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
1111
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
12+
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
1213
Scratch = "6c6a2e73-6563-6170-7368-637461726353"
14+
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
15+
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
1316
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
1417
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
1518

src/GPUCompiler.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ using ExprTools: splitdef, combinedef
99

1010
using Libdl
1111

12+
using Serialization
1213
using Scratch: @get_scratch!
14+
using Preferences
1315

1416
const CC = Core.Compiler
1517
using Core: MethodInstance, CodeInstance, CodeInfo

src/execution.jl

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,31 @@ end
6161

6262

6363
## cached compilation
64+
disk_cache() = parse(Bool, @load_preference("disk_cache", "false"))
65+
66+
"""
67+
enable_cache!(state::Bool=true)
68+
69+
Activate the GPUCompiler disk cache in the current environment.
70+
You will need to restart your Julia environment for it to take effect.
71+
72+
!!! note
73+
The cache functionality requires Julia 1.11
74+
"""
75+
function enable_cache!(state::Bool=true)
76+
@set_preferences!("disk_cache"=>string(state))
77+
end
78+
79+
cache_path() = @get_scratch!("cache")
80+
clear_disk_cache!() = rm(cache_path(); recursive=true, force=true)
81+
function cache_path(key)
82+
return joinpath(
83+
cache_path(),
84+
# TODO: Use object_build_id from https://github.com/JuliaLang/julia/pull/53943
85+
# Should we disk cache "runtime compilation".
86+
string(Base.module_build_id(GPUCompiler)), # captures dependencies as well
87+
string(cache_key), "ir.jls")
88+
end
6489

6590
const cache_lock = ReentrantLock()
6691

@@ -117,20 +142,42 @@ end
117142
ci = ci_cache_lookup(ci_cache(job), src, world, world)::Union{Nothing,CodeInstance}
118143
if ci !== nothing
119144
key = (ci, cfg)
120-
if haskey(cache, key)
121-
obj = cache[key]
122-
end
145+
obj = get(cache, key, nothing)
123146
end
124147

125148
# slow path: compile and link
126149
if obj === nothing || compile_hook[] !== nothing
127-
# TODO: consider loading the assembly from an on-disk cache here
128-
asm = compiler(job)
150+
asm = nothing
151+
@static if VERSION >= v"1.11.0-" && disk_cache()
152+
cache_key = Base.objectid(ci)
153+
path = cache_path(cache_key)
154+
if isfile(path)
155+
try
156+
@debug "Loading compiled kernel for $spec from $path"
157+
asm = deserialize(path)
158+
catch ex
159+
@warn "Failed to load compiled kernel at $path" exception=(ex, catch_backtrace())
160+
end
161+
end
162+
else
129163

164+
if asm === nothing
165+
asm = compiler(job)
166+
end
130167
if obj !== nothing
131168
# we got here because of a *compile* hook; don't bother linking
132169
return obj
133170
end
171+
172+
@static if VERSION >= v"1.11.0-" && disk_cache() && !isfile(path)
173+
# TODO: Should we only write out during precompilation?
174+
tmppath, io = mktemp(;cleanup=false)
175+
serialize(io, asm)
176+
close(io)
177+
# atomic move
178+
Base.rename(tmppath, path, force=true)
179+
end
180+
134181
obj = linker(job, asm)
135182

136183
if ci === nothing

test/CacheEnv/LocalPreferences.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[GPUCompiler]
2+
disk_cache = "true"
3+
cache_key = "test"

test/CacheEnv/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[extras]
2+
GPUCompiler = "61eb1bfa-7361-4325-ad38-22787b887f55"

test/cache.jl

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# @testset "Disk cache" begin
2+
# @test GPUCompiler.disk_cache() == false
3+
# cmd = Base.julia_cmd()
4+
# if Base.JLOptions().project != C_NULL
5+
# cmd = `$cmd --project=$(unsafe_string(Base.JLOptions().project))`
6+
# end
7+
8+
# withenv("JULIA_LOAD_PATH" => "$(get(ENV, "JULIA_LOAD_PATH", "")):$(joinpath(@__DIR__, "CacheEnv"))") do
9+
# @test success(pipeline(`$cmd cache.jl true`, stderr=stderr, stdout=stdout))
10+
# @test success(pipeline(`$cmd cache.jl false`, stderr=stderr, stdout=stdout))
11+
# end
12+
# end
13+
14+
15+
using GPUCompiler
16+
using Test
17+
18+
const TOTAL_KERNELS = 1
19+
20+
clear = parse(Bool, ARGS[1])
21+
22+
@test GPUCompiler.disk_cache() == true
23+
24+
if clear
25+
GPUCompiler.clear_disk_cache!()
26+
@test length(readdir(GPUCompiler.cache_path())) == 0
27+
else
28+
@test length(readdir(GPUCompiler.cache_path())) == TOTAL_KERNELS
29+
end
30+
31+
using LLVM, LLVM.Interop
32+
33+
include("util.jl")
34+
include("definitions/native.jl")
35+
36+
kernel() = return
37+
38+
const runtime_cache = Dict{UInt, Any}()
39+
40+
function compiler(job)
41+
return GPUCompiler.compile(:asm, job)
42+
end
43+
44+
function linker(job, asm)
45+
asm
46+
end
47+
48+
let (job, kwargs) = native_job(kernel, Tuple{})
49+
source = job.source
50+
config = job.config
51+
GPUCompiler.cached_compilation(runtime_cache, config, source.ft, source.tt, compiler, linker)
52+
end
53+
54+
@test length(readdir(GPUCompiler.cache_path())) == TOTAL_KERNELS

0 commit comments

Comments
 (0)