Skip to content

Commit cfb1579

Browse files
d-burgclaude
andcommitted
ForceFreeStates - MINOR - Make the corpus runner's worker calls serializable
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 7a2b947 commit cfb1579

1 file changed

Lines changed: 48 additions & 18 deletions

File tree

benchmarks/run_geqdsk_corpus.jl

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,29 @@ function parse_args(args)
3131
while i <= length(args)
3232
a = args[i]
3333
if a == "--list"
34-
opts["list"] = abspath(args[i+1]); i += 2
34+
opts["list"] = abspath(args[i+1])
35+
i += 2
3536
elseif a == "--root"
36-
opts["root"] = abspath(args[i+1]); i += 2
37+
opts["root"] = abspath(args[i+1])
38+
i += 2
3739
elseif a == "--out"
38-
opts["out"] = abspath(args[i+1]); i += 2
40+
opts["out"] = abspath(args[i+1])
41+
i += 2
3942
elseif a == "--template"
40-
opts["template"] = abspath(args[i+1]); i += 2
43+
opts["template"] = abspath(args[i+1])
44+
i += 2
4145
elseif a == "--workers"
42-
opts["workers"] = parse(Int, args[i+1]); i += 2
46+
opts["workers"] = parse(Int, args[i+1])
47+
i += 2
4348
elseif a == "--threads"
44-
opts["threads"] = parse(Int, args[i+1]); i += 2
49+
opts["threads"] = parse(Int, args[i+1])
50+
i += 2
4551
elseif a == "--timeout"
46-
opts["timeout"] = parse(Float64, args[i+1]); i += 2
52+
opts["timeout"] = parse(Float64, args[i+1])
53+
i += 2
4754
elseif a == "--limit"
48-
opts["limit"] = parse(Int, args[i+1]); i += 2
55+
opts["limit"] = parse(Int, args[i+1])
56+
i += 2
4957
else
5058
error("unknown argument $a")
5159
end
@@ -77,16 +85,19 @@ function main(args)
7785
template = corpus_template(opts["template"])
7886
exeflags = ["--project=$(Base.active_project())", "--threads=$(opts["threads"])"]
7987

80-
# Worker-side case runner; defined on each worker after it is spawned.
81-
worker_setup = quote
88+
# Worker-side case runner; the imports are evaluated first so the macros below resolve.
89+
worker_imports = quote
8290
using LinearAlgebra, TOML, Printf
8391
using GeneralizedPerturbedEquilibrium
92+
end
93+
worker_setup = quote
8494
const GPE = GeneralizedPerturbedEquilibrium
8595
const FFS = GPE.ForceFreeStates
8696
function read_q_profile(path)
8797
lines = readlines(path)
8898
hdr = lines[1]
89-
nw = parse(Int, hdr[53:56]); nh = parse(Int, hdr[57:60])
99+
nw = parse(Int, hdr[53:56])
100+
nh = parse(Int, hdr[57:60])
90101
nums = Float64[]
91102
for l in lines[2:end]
92103
for m in eachmatch(r"[-+]?\d*\.\d+(?:[eE][-+]?\d+)?", l)
@@ -137,23 +148,42 @@ function main(args)
137148
total_steps=odet.total_steps, t_equil, t_prep, t_int, t_free, t_dp,
138149
et1=get(et, 1, NaN), et2=get(et, 2, NaN), et3=get(et, 3, NaN), dp, message="")
139150
end
151+
# Errors are turned into a row on the worker: a raised exception would carry method
152+
# instances the driver process cannot deserialize.
153+
function run_case_safe(geqdsk::String, template::Dict{String,Any})
154+
t0 = time()
155+
try
156+
return run_case(geqdsk, template)
157+
catch err
158+
msg = first(sprint(showerror, err), 400)
159+
return (status="failed", elapsed=time() - t0, nw=0, q0=NaN, qmin=NaN, psi_qmin=NaN, qedge=NaN, N=0, msing=0, surfaces="",
160+
total_steps=0, t_equil=NaN, t_prep=NaN, t_int=NaN, t_free=NaN, t_dp=NaN, et1=NaN, et2=NaN, et3=NaN, dp="", message=msg)
161+
end
162+
end
163+
nothing # the eval's return value travels back to the driver; a function object would not deserialize there
140164
end
141165

142166
function spawn_worker()
143167
pid = only(addprocs(1; exeflags=exeflags))
168+
remotecall_fetch(Core.eval, pid, Main, worker_imports)
144169
remotecall_fetch(Core.eval, pid, Main, worker_setup)
170+
remotecall_fetch(Core.eval, pid, Main, :(const TEMPLATE = $template))
145171
return pid
146172
end
147173

148174
io = open(opts["out"], "w")
149175
println(io, HEADER)
150176
flush(io)
151177
write_row(idx, rel, r) = begin
152-
println(io, join([idx, rel, r.status, @sprintf("%.1f", r.elapsed), r.nw, @sprintf("%.4f", r.q0), @sprintf("%.4f", r.qmin),
153-
@sprintf("%.3f", r.psi_qmin), @sprintf("%.3f", r.qedge), r.N, r.msing, r.surfaces, r.total_steps,
154-
@sprintf("%.2f", r.t_equil), @sprintf("%.2f", r.t_prep), @sprintf("%.2f", r.t_int), @sprintf("%.2f", r.t_free),
155-
@sprintf("%.2f", r.t_dp), @sprintf("%.8e", r.et1), @sprintf("%.8e", r.et2), @sprintf("%.8e", r.et3), r.dp,
156-
replace(r.message, "," => ";", "\n" => " ")], ","))
178+
println(
179+
io,
180+
join(
181+
[idx, rel, r.status, @sprintf("%.1f", r.elapsed), r.nw, @sprintf("%.4f", r.q0), @sprintf("%.4f", r.qmin),
182+
@sprintf("%.3f", r.psi_qmin), @sprintf("%.3f", r.qedge), r.N, r.msing, r.surfaces, r.total_steps,
183+
@sprintf("%.2f", r.t_equil), @sprintf("%.2f", r.t_prep), @sprintf("%.2f", r.t_int), @sprintf("%.2f", r.t_free),
184+
@sprintf("%.2f", r.t_dp), @sprintf("%.8e", r.et1), @sprintf("%.8e", r.et2), @sprintf("%.8e", r.et3), r.dp,
185+
replace(r.message, "," => ";", "\n" => " ")], ",")
186+
)
157187
flush(io)
158188
end
159189
blank(status, elapsed, msg) = (status, elapsed, nw=0, q0=NaN, qmin=NaN, psi_qmin=NaN, qedge=NaN, N=0, msing=0, surfaces="", total_steps=0,
@@ -172,15 +202,15 @@ function main(args)
172202
for (idx, rel) in queue
173203
path = joinpath(opts["root"], rel)
174204
t0 = time()
175-
task = @async remotecall_fetch(Main.run_case, pid, path, template)
205+
task = @async remotecall_fetch(Core.eval, pid, Main, :(run_case_safe($path, TEMPLATE)))
176206
while !istaskdone(task) && time() - t0 < opts["timeout"]
177207
sleep(1)
178208
end
179209
r = if istaskdone(task)
180210
try
181211
fetch(task)
182212
catch err
183-
blank("failed", time() - t0, sprint(showerror, err)[1:min(end, 300)])
213+
blank("failed", time() - t0, first(sprint(showerror, err), 300))
184214
end
185215
else
186216
rmprocs(pid; waitfor=5)

0 commit comments

Comments
 (0)