Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use uv_thread_getaffinity when --threads=auto #42340

Merged
merged 27 commits into from
Feb 19, 2022
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4a8c738
Use uv_thread_getaffinity to bound nthreads
tkf Sep 21, 2021
4309748
Add some safepoint annotations
tkf Sep 22, 2021
d2df381
Use ternary expression
tkf Sep 23, 2021
eaf1908
Ignore EBADF on Windows
tkf Sep 23, 2021
b6ba167
Merge branch 'master' into useaffinity
tkf Dec 15, 2021
68bc481
Revert "Ignore EBADF on Windows"
tkf Dec 15, 2021
1221ea3
Merge branch 'master' into useaffinity
tkf Dec 16, 2021
2d22485
Merge branch 'master' into useaffinity
tkf Jan 13, 2022
c9b3ca5
Add jl_effective_threads
tkf Jan 13, 2022
dcd12d6
Merge branch 'master' into useaffinity
tkf Jan 16, 2022
a1572a2
Test affinity-based nthreads setup
tkf Jan 16, 2022
c2d3f9b
Tweak testing
tkf Jan 17, 2022
e64ff3a
Compare output without affinity setting
tkf Jan 17, 2022
81b1d66
DEBUG: print input/output of get_nthreads
tkf Jan 17, 2022
fc655aa
Merge branch 'master' into useaffinity
tkf Jan 17, 2022
4f65152
Document how `--threads=auto` now works
tkf Jan 17, 2022
c3039fc
Revert "DEBUG: print input/output of get_nthreads"
tkf Jan 17, 2022
f7252ec
Don't run get_nthreads test under rr
tkf Jan 17, 2022
140654c
Better error handling
tkf Jan 26, 2022
8c33dc1
Check if running under rr
tkf Jan 28, 2022
2375873
Mention -t=auto behavior elsewhere
tkf Jan 28, 2022
85cba54
Mention it in NEWS
tkf Jan 28, 2022
c37c34c
Merge branch 'master' into useaffinity
tkf Jan 28, 2022
cdd8223
Merge branch 'master' into useaffinity
tkf Jan 29, 2022
b263a8d
Check affinity support in one place
tkf Jan 30, 2022
ae74887
Merge branch 'master' into useaffinity
tkf Feb 17, 2022
3e2b70f
Merge branch 'master' into useaffinity
tkf Feb 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ Command-line option changes
code when building a system image. The resulting image will only work if `--compile=all` is
used, or if all needed code is precompiled ([#42925]).
* When the program file is `-` the code to be executed is read from standard in ([#43191]).
* In Linux and Windows, `--threads=auto` now tries to infer usable number of CPUs from the
process affinity which is set typically in HPC and cloud environments ([#42340]).

Multi-threading changes
-----------------------
Expand Down
8 changes: 7 additions & 1 deletion doc/man/julia.1
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,13 @@ Load <file> immediately on all processors

.TP
-t, --threads <n>
Enable n threads
Enable n threads; "auto" tries to infer a useful default number
of threads to use but the exact behavior might change in the future.
Currently, "auto" uses the number of CPUs assigned to this julia
process based on the OS-specific affinity assignment interface, if
supported (Linux and Windows). If this is not supported (macOS) or
process affinity is not configured, it uses the number of CPU
threads.

.TP
-p, --procs <n>
Expand Down
4 changes: 3 additions & 1 deletion doc/src/manual/command-line-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ The following is a complete list of command-line switches available when launchi
|`-e`, `--eval <expr>` |Evaluate `<expr>`|
|`-E`, `--print <expr>` |Evaluate `<expr>` and display the result|
|`-L`, `--load <file>` |Load `<file>` immediately on all processors|
|`-t`, `--threads {N\|auto`} |Enable N threads; `auto` currently sets N to the number of local CPU threads but this might change in the future|
|`-t`, `--threads {N\|auto`} |Enable N threads; `auto` tries to infer a useful default number of threads to use but the exact behavior might change in the future. Currently, `auto` uses the number of CPUs assigned to this julia process based on the OS-specific affinity assignment interface, if supported (Linux and Windows). If this is not supported (macOS) or process affinity is not configured, it uses the number of CPU threads.|
|`-p`, `--procs {N\|auto`} |Integer value N launches N additional local worker processes; `auto` launches as many workers as the number of local CPU threads (logical cores)|
|`--machine-file <file>` |Run processes on hosts listed in `<file>`|
|`-i` |Interactive mode; REPL runs and `isinteractive()` is true|
Expand All @@ -111,6 +111,8 @@ The following is a complete list of command-line switches available when launchi
|`--track-allocation={none\|user\|all}` |Count bytes allocated by each source line|
|`--track-allocation` |equivalent to `--track-allocation=user`|



!!! compat "Julia 1.1"
In Julia 1.0, the default `--project=@.` option did not search up from the root
directory of a Git repository for the `Project.toml` file. From Julia 1.1 forward, it
Expand Down
4 changes: 2 additions & 2 deletions doc/src/manual/multi-threading.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ The number of execution threads is controlled either by using the
specified, then `-t`/`--threads` takes precedence.

The number of threads can either be specified as an integer (`--threads=4`) or as `auto`
(`--threads=auto`), where `auto` sets the number of threads to the number of local CPU
threads.
(`--threads=auto`), where `auto` tries to infer a useful default number of threads to use
(see [Command-line Options](@id command-line-options) for more details).

!!! compat "Julia 1.5"
The `-t`/`--threads` command line argument requires at least Julia 1.5.
Expand Down
11 changes: 8 additions & 3 deletions src/jloptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,13 @@ static const char opts[] =
" -L, --load <file> Load <file> immediately on all processors\n\n"

// parallel options
" -t, --threads {N|auto} Enable N threads; \"auto\" currently sets N to the number of local\n"
" CPU threads but this might change in the future\n"
" -t, --threads {N|auto} Enable N threads; \"auto\" tries to infer a useful default number\n"
tkf marked this conversation as resolved.
Show resolved Hide resolved
" of threads to use but the exact behavior might change in the future.\n"
" Currently, \"auto\" uses the number of CPUs assigned to this julia\n"
" process based on the OS-specific affinity assignment interface, if\n"
" supported (Linux and Windows). If this is not supported (macOS) or\n"
" process affinity is not configured, it uses the number of CPU\n"
" threads.\n"
" -p, --procs {N|auto} Integer value N launches N additional local worker processes\n"
" \"auto\" launches as many workers as the number of local CPU threads (logical cores)\n"
" --machine-file <file> Run processes on hosts listed in <file>\n\n"
Expand Down Expand Up @@ -441,7 +446,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
case 'p': // procs
errno = 0;
if (!strcmp(optarg,"auto")) {
jl_options.nprocs = jl_cpu_threads();
jl_options.nprocs = jl_effective_threads();
}
else {
long nprocs = strtol(optarg, &endptr, 10);
Expand Down
1 change: 1 addition & 0 deletions src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,7 @@ extern JL_DLLEXPORT ssize_t jl_tls_offset;
extern JL_DLLEXPORT const int jl_tls_elf_support;
void jl_init_threading(void);
void jl_start_threads(void);
int jl_effective_threads(void);

// Whether the GC is running
extern char *jl_safepoint_pages;
Expand Down
23 changes: 23 additions & 0 deletions src/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,29 @@ JL_DLLEXPORT int jl_cpu_threads(void) JL_NOTSAFEPOINT
#endif
}

int jl_effective_threads(void) JL_NOTSAFEPOINT
{
int cpu = jl_cpu_threads();
int masksize = uv_cpumask_size();
if (masksize < 0 || jl_running_under_rr(0))
return cpu;
uv_thread_t tid = uv_thread_self();
char *cpumask = (char *)calloc(masksize, sizeof(char));
int err = uv_thread_getaffinity(&tid, cpumask, masksize);
if (err) {
free(cpumask);
jl_safe_printf("WARNING: failed to get thread affinity (%s %d)\n", uv_err_name(err),
err);
return cpu;
}
int n = 0;
for (size_t i = 0; i < masksize; i++) {
n += cpumask[i];
}
free(cpumask);
return n < cpu ? n : cpu;
}


// -- high resolution timers --
// Returns time in nanosec
Expand Down
4 changes: 2 additions & 2 deletions src/threading.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ void jl_init_threading(void)
// how many threads available, usable
jl_n_threads = JULIA_NUM_THREADS;
if (jl_options.nthreads < 0) { // --threads=auto
jl_n_threads = jl_cpu_threads();
jl_n_threads = jl_effective_threads();
}
else if (jl_options.nthreads > 0) { // --threads=N
jl_n_threads = jl_options.nthreads;
Expand All @@ -463,7 +463,7 @@ void jl_init_threading(void)
if (strcmp(cp, "auto"))
jl_n_threads = (uint64_t)strtol(cp, NULL, 10); // ENV[NUM_THREADS_NAME] == "N"
else
jl_n_threads = jl_cpu_threads(); // ENV[NUM_THREADS_NAME] == "auto"
jl_n_threads = jl_effective_threads(); // ENV[NUM_THREADS_NAME] == "auto"
}
if (jl_n_threads <= 0)
jl_n_threads = 1;
Expand Down
27 changes: 27 additions & 0 deletions test/threads.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,33 @@ if Sys.islinux() || Sys.iswindows()
end
end

function get_nthreads(options = ``; cpus = nothing)
cmd = `$(Base.julia_cmd()) --startup-file=no $(options)`
cmd = `$cmd -e "print(Threads.nthreads())"`
cmd = addenv(cmd, "JULIA_EXCLUSIVE" => "0", "JULIA_NUM_THREADS" => "auto")
if cpus !== nothing
cmd = setcpuaffinity(cmd, cpus)
end
return parse(Int, read(cmd, String))
end

@testset "nthreads determined based on CPU affinity" begin
if !Sys.isapple() && !running_under_rr() && Sys.CPU_THREADS ≥ 2
@test get_nthreads() ≥ 2
@test get_nthreads(cpus = [1]) == 1
@test get_nthreads(cpus = [2]) == 1
@test get_nthreads(cpus = [1, 2]) == 2
@test get_nthreads(`-t1`, cpus = [1]) == 1
@test get_nthreads(`-t1`, cpus = [2]) == 1
@test get_nthreads(`-t1`, cpus = [1, 2]) == 1

if Sys.CPU_THREADS ≥ 3
@test get_nthreads(cpus = [1, 3]) == 2
@test get_nthreads(cpus = [2, 3]) == 2
end
end
end

# issue #34769
function idle_callback(handle)
idle = @Base.handle_as handle UvTestIdle
Expand Down