/
workloads.jl
143 lines (127 loc) · 4.62 KB
/
workloads.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
function workload_enabled(mod::Module)
try
if load_preference(@__MODULE__, "precompile_workloads", true)
return load_preference(mod, "precompile_workload", true)
else
return false
end
catch
true
end
end
"""
check_edges(node)
Recursively ensure that all callees of `node` are precompiled. This is (rarely) necessary
because sometimes there is no backedge from callee to caller (xref https://github.com/JuliaLang/julia/issues/49617),
and `staticdata.c` relies on the backedge to trace back to a MethodInstance that is tagged `mi.precompiled`.
"""
function check_edges(node)
parentmi = node.mi_info.mi
for child in node.children
childmi = child.mi_info.mi
if !(isdefined(childmi, :backedges) && parentmi ∈ childmi.backedges)
precompile_mi(childmi)
end
check_edges(child)
end
end
function precompile_roots(roots)
@assert have_inference_tracking
for child in roots
precompile_mi(child.mi_info.mi)
check_edges(child)
end
end
"""
@compile_workload f(args...)
`precompile` (and save in the compile_workload file) any method-calls that occur inside the expression. All calls (direct or indirect) inside a
`@compile_workload` block will be cached.
`@compile_workload` has three key features:
1. code inside runs only when the package is being precompiled (i.e., a `*.ji`
precompile compile_workload file is being written)
2. the interpreter is disabled, ensuring your calls will be compiled
3. both direct and indirect callees will be precompiled, even for methods defined in other packages
and even for runtime-dispatched callees (requires Julia 1.8 and above).
!!! note
For comprehensive precompilation, ensure the first usage of a given method/argument-type combination
occurs inside `@compile_workload`.
In detail: runtime-dispatched callees are captured only when type-inference is executed, and they
are inferred only on first usage. Inferrable calls that trace back to a method defined in your package,
and their *inferrable* callees, will be precompiled regardless of "ownership" of the callees
(Julia 1.8 and higher).
Consequently, this recommendation matters only for:
- direct calls to methods defined in Base or other packages OR
- indirect runtime-dispatched calls to such methods.
"""
macro compile_workload(ex::Expr)
local iscompiling = if Base.VERSION < v"1.6"
:(ccall(:jl_generating_output, Cint, ()) == 1)
else
:((ccall(:jl_generating_output, Cint, ()) == 1 && $PrecompileTools.workload_enabled(@__MODULE__)))
end
if have_force_compile
ex = quote
begin
Base.Experimental.@force_compile
$(esc(ex))
end
end
else
# Use the hack on earlier Julia versions that blocks the interpreter
ex = quote
while false end
$(esc(ex))
end
end
if have_inference_tracking
ex = quote
Core.Compiler.Timings.reset_timings()
Core.Compiler.__set_measure_typeinf(true)
try
$ex
finally
Core.Compiler.__set_measure_typeinf(false)
Core.Compiler.Timings.close_current_timer()
end
$PrecompileTools.precompile_roots(Core.Compiler.Timings._timings[1].children)
end
end
return quote
if $iscompiling || $PrecompileTools.verbose[]
$ex
end
end
end
"""
@setup_workload begin
vars = ...
⋮
end
Run the code block only during package precompilation. `@setup_workload` is often used in combination
with [`@compile_workload`](@ref), for example:
@setup_workload begin
vars = ...
@compile_workload begin
y = f(vars...)
g(y)
⋮
end
end
`@setup_workload` does not force compilation (though it may happen anyway) nor intentionally capture
runtime dispatches (though they will be precompiled anyway if the runtime-callee is for a method belonging
to your package).
"""
macro setup_workload(ex::Expr)
local iscompiling = if Base.VERSION < v"1.6"
:(ccall(:jl_generating_output, Cint, ()) == 1)
else
:((ccall(:jl_generating_output, Cint, ()) == 1 && $PrecompileTools.workload_enabled(@__MODULE__)))
end
# Ideally we'd like a `let` around this to prevent namespace pollution, but that seem to
# trigger inference & codegen in undesirable ways (see #16).
return quote
if $iscompiling || $PrecompileTools.verbose[]
$(esc(ex))
end
end
end