Skip to content

Commit 560e140

Browse files
committed
Add disk cache infrastructure for Julia 1.11
1 parent 3c1bc65 commit 560e140

File tree

6 files changed

+117
-5
lines changed

6 files changed

+117
-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: 53 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

@@ -115,19 +140,42 @@ end
115140

116141
# fast path: find an applicable CodeInstance and see if we have compiled it before
117142
ci = ci_cache_lookup(ci_cache(job), src, world, world)::Union{Nothing,CodeInstance}
118-
if ci !== nothing && haskey(cache, ci)
119-
obj = cache[ci]
143+
if ci !== nothing
144+
obj = get(cache, ci, nothing)
120145
end
121146

122147
# slow path: compile and link
123148
if obj === nothing || compile_hook[] !== nothing
124-
# TODO: consider loading the assembly from an on-disk cache here
125-
asm = compiler(job)
126-
127149
if obj !== nothing
128150
# we got here because of a *compile* hook; don't bother linking
129151
return obj
130152
end
153+
asm = nothing
154+
@static if VERSION >= v"1.11.0-" && disk_cache()
155+
cache_key = Base.objectid(ci)
156+
path = cache_path(cache_key)
157+
if isfile(path)
158+
try
159+
@debug "Loading compiled kernel for $spec from $path"
160+
asm = deserialize(path)
161+
catch ex
162+
@warn "Failed to load compiled kernel at $path" exception=(ex, catch_backtrace())
163+
end
164+
end
165+
else
166+
167+
if asm === nothing
168+
asm = compiler(job)
169+
end
170+
171+
@static if VERSION >= v"1.11.0-" && disk_cache() && !isfile(path)
172+
# TODO: Should we only write out during precompilation?
173+
tmppath, io = mktemp(;cleanup=false)
174+
serialize(io, asm)
175+
close(io)
176+
# atomic move
177+
Base.rename(tmppath, path, force=true)
178+
end
131179

132180
obj = linker(job, asm)
133181
ci = ci_cache_lookup(ci_cache(job), src, world, world)::CodeInstance

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)