Skip to content

Commit 91f8020

Browse files
authored
Make REPL not slow-down if we load a valid cachefile (#51565)
Fixes #51532. We have funny series of interactions. With the REPL and its dependencies being removed from the system image, we observed latency regressions when users create their own precompilation cache of REPL. During the precompilation of REPL we launch a subordinate process that we send statements too. Now we do want that process to use the existing cache of the REPL dependencies so we launch it with `--compiled-modules=existing`. Otherwise precompilation of REPL is even slower than it is now. When the user triggers recompilation of REPL due to the use of `-O3` the subordinate process sees a valid cache file for REPL itself. Thus no (or very few) precompilation statements are being generated. Leading to the cache file compiled with `-O3` to have a significant latency regression. In this PR I work around this by replaying the precompilation statements of REPL from the subordinate process. A bit hacky, but should be more reliable than trying to set up a "just right" depot, or filtering the REPL cache-file out.
1 parent 0fd7f72 commit 91f8020

File tree

4 files changed

+45
-12
lines changed

4 files changed

+45
-12
lines changed

src/gf.c

+30-11
Original file line numberDiff line numberDiff line change
@@ -2328,11 +2328,24 @@ jl_code_instance_t *jl_method_compiled(jl_method_instance_t *mi, size_t world)
23282328
}
23292329

23302330
jl_mutex_t precomp_statement_out_lock;
2331+
ios_t f_precompile;
2332+
JL_STREAM* s_precompile = NULL;
2333+
2334+
static void init_precompile_output(void)
2335+
{
2336+
const char *t = jl_options.trace_compile;
2337+
if (!strncmp(t, "stderr", 6)) {
2338+
s_precompile = JL_STDERR;
2339+
}
2340+
else {
2341+
if (ios_file(&f_precompile, t, 1, 1, 1, 1) == NULL)
2342+
jl_errorf("cannot open precompile statement file \"%s\" for writing", t);
2343+
s_precompile = (JL_STREAM*) &f_precompile;
2344+
}
2345+
}
23312346

23322347
static void record_precompile_statement(jl_method_instance_t *mi)
23332348
{
2334-
static ios_t f_precompile;
2335-
static JL_STREAM* s_precompile = NULL;
23362349
jl_method_t *def = mi->def.method;
23372350
if (jl_options.trace_compile == NULL)
23382351
return;
@@ -2341,15 +2354,7 @@ static void record_precompile_statement(jl_method_instance_t *mi)
23412354

23422355
JL_LOCK(&precomp_statement_out_lock);
23432356
if (s_precompile == NULL) {
2344-
const char *t = jl_options.trace_compile;
2345-
if (!strncmp(t, "stderr", 6)) {
2346-
s_precompile = JL_STDERR;
2347-
}
2348-
else {
2349-
if (ios_file(&f_precompile, t, 1, 1, 1, 1) == NULL)
2350-
jl_errorf("cannot open precompile statement file \"%s\" for writing", t);
2351-
s_precompile = (JL_STREAM*) &f_precompile;
2352-
}
2357+
init_precompile_output();
23532358
}
23542359
if (!jl_has_free_typevars(mi->specTypes)) {
23552360
jl_printf(s_precompile, "precompile(");
@@ -2361,6 +2366,20 @@ static void record_precompile_statement(jl_method_instance_t *mi)
23612366
JL_UNLOCK(&precomp_statement_out_lock);
23622367
}
23632368

2369+
JL_DLLEXPORT void jl_write_precompile_statement(char* statement)
2370+
{
2371+
if (jl_options.trace_compile == NULL)
2372+
return;
2373+
JL_LOCK(&precomp_statement_out_lock);
2374+
if (s_precompile == NULL) {
2375+
init_precompile_output();
2376+
}
2377+
jl_printf(s_precompile, "%s\n", statement);
2378+
if (s_precompile != JL_STDERR)
2379+
ios_flush(&f_precompile);
2380+
JL_UNLOCK(&precomp_statement_out_lock);
2381+
}
2382+
23642383
jl_method_instance_t *jl_normalize_to_compilable_mi(jl_method_instance_t *mi JL_PROPAGATES_ROOT);
23652384

23662385
jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t world)

src/jl_exported_funcs.inc

+1
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@
363363
XX(jl_new_typevar) \
364364
XX(jl_next_from_addrinfo) \
365365
XX(jl_normalize_to_compilable_sig) \
366+
XX(jl_write_precompile_statement) \
366367
XX(jl_no_exc_handler) \
367368
XX(jl_object_id) \
368369
XX(jl_object_id_) \

stdlib/REPL/src/REPL.jl

+13
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,21 @@ REPL.run_repl(repl)
1414
"""
1515
module REPL
1616

17+
const PRECOMPILE_STATEMENTS = Vector{String}()
18+
1719
function __init__()
1820
Base.REPL_MODULE_REF[] = REPL
21+
# We can encounter the situation where the sub-ordinate process used
22+
# during precompilation of REPL, can load a valid cache-file.
23+
# We need to replay the statements such that the parent process
24+
# can also include those. See JuliaLang/julia#51532
25+
if Base.JLOptions().trace_compile !== C_NULL && !isempty(PRECOMPILE_STATEMENTS)
26+
for statement in PRECOMPILE_STATEMENTS
27+
ccall(:jl_write_precompile_statement, Cvoid, (Cstring,), statement)
28+
end
29+
else
30+
empty!(PRECOMPILE_STATEMENTS)
31+
end
1932
end
2033

2134
Base.Experimental.@optlevel 1

stdlib/REPL/src/precompile.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,9 @@ generate_precompile_statements() = try
176176
if !isexpr(ps, :call)
177177
# these are typically comments
178178
@debug "skipping statement because it does not parse as an expression" statement
179-
delete!(statements, statement)
180179
continue
181180
end
181+
push!(REPL.PRECOMPILE_STATEMENTS, statement)
182182
popfirst!(ps.args) # precompile(...)
183183
ps.head = :tuple
184184
# println(ps)

0 commit comments

Comments
 (0)