Skip to content

Commit

Permalink
Implement [-t|--threads] command line argument for specifying (JuliaL…
Browse files Browse the repository at this point in the history
…ang#35108)

the number of Julia threads on startup, fixes JuliaLang#26889.
  • Loading branch information
fredrikekre authored Apr 10, 2020
1 parent 8a55a27 commit a6a2d26
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 22 deletions.
9 changes: 9 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ Language changes

* Color now defaults to on when stdout and stderr are TTYs ([#34347])

Command-line option changes
---------------------------

* `-t N`, `--threads N` starts Julia with `N` threads. This option takes precedence over
`JULIA_NUM_THREADS`. The specified number of threads also propagates to worker
processes spawned using the `-p`/`--procs` or `--machine-file` command line arguments.
In order to set number of threads for worker processes spawned with `addprocs` use the
`exeflags` keyword argument, e.g. `` addprocs(...; exeflags=`--threads 4`) `` ([#35108]).

Multi-threading changes
-----------------------

Expand Down
1 change: 1 addition & 0 deletions base/options.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ struct JLOptions
commands::Ptr{Ptr{UInt8}} # (e)eval, (E)print, (L)load
image_file::Ptr{UInt8}
cpu_target::Ptr{UInt8}
nthreads::Int32
nprocs::Int32
machine_file::Ptr{UInt8}
project::Ptr{UInt8}
Expand Down
4 changes: 4 additions & 0 deletions doc/man/julia.1
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ Evaluate <expr> and display the result
-L, --load <file>
Load <file> immediately on all processors

.TP
-t, --threads <n>
Enable n threads

.TP
-p, --procs <n>
Run n local processes
Expand Down
6 changes: 5 additions & 1 deletion doc/src/manual/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ A [`Float64`](@ref) that sets the value of `Distributed.worker_timeout()` (defau
This function gives the number of seconds a worker process will wait for
a master process to establish a connection before dying.

### `JULIA_NUM_THREADS`
### [`JULIA_NUM_THREADS`](@id JULIA_NUM_THREADS)

An unsigned 64-bit integer (`uint64_t`) that sets the maximum number of threads
available to Julia. If `$JULIA_NUM_THREADS` exceeds the number of available
Expand All @@ -195,6 +195,10 @@ set to `1`.

`JULIA_NUM_THREADS` must be defined before starting julia; defining it in `startup.jl` is too late in the startup process.

!!! compat "Julia 1.5"
In Julia 1.5 and above the number of threads can also be specified on startup
using the `-t`/`--threads` command line argument.

### `JULIA_THREAD_SLEEP_THRESHOLD`

If set to a string that starts with the case-insensitive substring `"infinite"`,
Expand Down
1 change: 1 addition & 0 deletions doc/src/manual/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ julia [switches] -- [programfile] [args...]
|`-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|
|`-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 Down
48 changes: 32 additions & 16 deletions doc/src/manual/parallel-computing.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,28 +231,21 @@ julia> Threads.nthreads()
1
```

The number of threads Julia starts up with is controlled by an environment variable called `JULIA_NUM_THREADS`.
Now, let's start up Julia with 4 threads:
The number of threads Julia starts up with is controlled either by using the
`-t`/`--threads` command line argument or by using the
[`JULIA_NUM_THREADS`](@ref JULIA_NUM_THREADS) environment variable. When both are
specified, then `-t`/`--threads` takes precedence.

Bash on Linux/OSX:
!!! compat "Julia 1.5"
The `-t`/`--threads` command line argument requires at least Julia 1.5.
In older versions you must use the environment variable instead.

```bash
export JULIA_NUM_THREADS=4
```

C shell on Linux/OSX, CMD on Windows:
Lets start Julia with 4 threads:

```bash
set JULIA_NUM_THREADS=4
$ julia --threads 4
```

Powershell on Windows:

```powershell
$env:JULIA_NUM_THREADS=4
```


Let's verify there are 4 threads at our disposal.

```julia-repl
Expand All @@ -267,6 +260,29 @@ julia> Threads.threadid()
1
```

!!! note
If you prefer to use the environment variable you can set it as follows in
Bash (Linux/macOS):
```bash
export JULIA_NUM_THREADS=4
```
C shell on Linux/macOS, CMD on Windows:
```bash
set JULIA_NUM_THREADS=4
```
Powershell on Windows:
```powershell
$env:JULIA_NUM_THREADS=4
```
Note that this must be done *before* starting Julia.

!!! note
The number of threads specified with `-t`/`--threads` is propagated to worker processes
that are spawned using the `-p`/`--procs` or `--machine-file` command line options.
For example, `julia -p2 -t2` spawns 1 main process with 2 worker processes, and all
three processes have 2 threads enabled. For more fine grained control over worker
threads use [`addprocs`](@ref) and pass `-t`/`--threads` as `exeflags`.

## The `@threads` Macro

Let's work a simple example using our native threads. Let us create an array of zeros:
Expand Down
19 changes: 18 additions & 1 deletion src/jloptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jl_options_t jl_options = { 0, // quiet
NULL, // cmds
NULL, // image_file (will be filled in below)
NULL, // cpu_target ("native", "core2", etc...)
0, // nthreads
0, // nprocs
NULL, // machine_file
NULL, // project
Expand Down Expand Up @@ -97,6 +98,9 @@ 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\" sets N to the number of local CPU 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 @@ -190,7 +194,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
opt_machine_file,
opt_project,
};
static const char* const shortopts = "+vhqH:e:E:L:J:C:ip:O:g:";
static const char* const shortopts = "+vhqH:e:E:L:J:C:it:p:O:g:";
static const struct option longopts[] = {
// exposed command line options
// NOTE: This set of required arguments need to be kept in sync
Expand All @@ -209,6 +213,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
{ "compiled-modules", required_argument, 0, opt_compiled_modules },
{ "cpu-target", required_argument, 0, 'C' },
{ "procs", required_argument, 0, 'p' },
{ "threads", required_argument, 0, 't' },
{ "machine-file", required_argument, 0, opt_machine_file },
{ "project", optional_argument, 0, opt_project },
{ "color", required_argument, 0, opt_color },
Expand Down Expand Up @@ -388,6 +393,18 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
if (!jl_options.cpu_target)
jl_error("julia: failed to allocate memory");
break;
case 't': // threads
errno = 0;
if (!strcmp(optarg,"auto")) {
jl_options.nthreads = -1;
}
else {
long nthreads = strtol(optarg, &endptr, 10);
if (errno != 0 || optarg == endptr || *endptr != 0 || nthreads < 1 || nthreads >= INT_MAX)
jl_errorf("julia: -t,--threads=<n> must be an integer >= 1");
jl_options.nthreads = (int)nthreads;
}
break;
case 'p': // procs
errno = 0;
if (!strcmp(optarg,"auto")) {
Expand Down
1 change: 1 addition & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -1900,6 +1900,7 @@ typedef struct {
const char **cmds;
const char *image_file;
const char *cpu_target;
int32_t nthreads;
int32_t nprocs;
const char *machine_file;
const char *project;
Expand Down
7 changes: 5 additions & 2 deletions src/threading.c
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,11 @@ void jl_init_threading(void)
// how many threads available, usable
int max_threads = jl_cpu_threads();
jl_n_threads = JULIA_NUM_THREADS;
cp = getenv(NUM_THREADS_NAME);
if (cp)
if (jl_options.nthreads < 0) // --threads=auto
jl_n_threads = max_threads;
else if (jl_options.nthreads > 0) // --threads=N
jl_n_threads = jl_options.nthreads;
else if ((cp = getenv(NUM_THREADS_NAME)))
jl_n_threads = (uint64_t)strtol(cp, NULL, 10);
if (jl_n_threads > max_threads)
jl_n_threads = max_threads;
Expand Down
8 changes: 6 additions & 2 deletions stdlib/Distributed/src/cluster.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1298,6 +1298,7 @@ end

write_cookie(io::IO) = print(io.in, string(cluster_cookie(), "\n"))

# Starts workers specified by (-n|--procs) and --machine-file command line options
function process_opts(opts)
# startup worker.
# opts.startupfile, opts.load, etc should should not be processed for workers.
Expand All @@ -1310,14 +1311,17 @@ function process_opts(opts)
end
end

# Propagate --threads to workers
exeflags = opts.nthreads > 0 ? `--threads=$(opts.nthreads)` : ``

# add processors
if opts.nprocs > 0
addprocs(opts.nprocs)
addprocs(opts.nprocs; exeflags=exeflags)
end

# load processes from machine file
if opts.machine_file != C_NULL
addprocs(load_machine_file(unsafe_string(opts.machine_file)))
addprocs(load_machine_file(unsafe_string(opts.machine_file)); exeflags=exeflags)
end
return nothing
end
Expand Down
26 changes: 26 additions & 0 deletions test/cmdlineargs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,32 @@ let exename = `$(Base.julia_cmd()) --startup-file=no`
@test !success(`$exename -C invalidtarget`)
@test !success(`$exename --cpu-target=invalidtarget`)

# -t, --threads
code = "print(Threads.nthreads())"
cpu_threads = ccall(:jl_cpu_threads, Int32, ())
@test string(cpu_threads) ==
read(`$exename --threads auto -e $code`, String) ==
read(`$exename --threads=auto -e $code`, String) ==
read(`$exename -tauto -e $code`, String) ==
read(`$exename -t auto -e $code`, String) ==
read(`$exename -t $(cpu_threads+1) -e $code`, String)
if cpu_threads > 1
for nt in (nothing, "1"); withenv("JULIA_NUM_THREADS"=>nt) do
@test read(`$exename --threads 2 -e $code`, String) ==
read(`$exename --threads=2 -e $code`, String) ==
read(`$exename -t2 -e $code`, String) ==
read(`$exename -t 2 -e $code`, String) == "2"
end end
end
@test !success(`$exename -t 0`)
@test !success(`$exename -t -1`)

# Combining --threads and --procs: --threads does propagate
if cpu_threads > 1; withenv("JULIA_NUM_THREADS"=>nothing) do
code = "print(sum(remotecall_fetch(Threads.nthreads, x) for x in procs()))"
@test read(`$exename -p2 -t2 -e $code`, String) == "6"
end end

# --procs
@test readchomp(`$exename -q -p 2 -e "println(nworkers())"`) == "2"
@test !success(`$exename -p 0`)
Expand Down

0 comments on commit a6a2d26

Please sign in to comment.