Skip to content

Commit a6a2d26

Browse files
authored
Implement [-t|--threads] command line argument for specifying (JuliaLang#35108)
the number of Julia threads on startup, fixes JuliaLang#26889.
1 parent 8a55a27 commit a6a2d26

File tree

11 files changed

+108
-22
lines changed

11 files changed

+108
-22
lines changed

NEWS.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ Language changes
7272

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

75+
Command-line option changes
76+
---------------------------
77+
78+
* `-t N`, `--threads N` starts Julia with `N` threads. This option takes precedence over
79+
`JULIA_NUM_THREADS`. The specified number of threads also propagates to worker
80+
processes spawned using the `-p`/`--procs` or `--machine-file` command line arguments.
81+
In order to set number of threads for worker processes spawned with `addprocs` use the
82+
`exeflags` keyword argument, e.g. `` addprocs(...; exeflags=`--threads 4`) `` ([#35108]).
83+
7584
Multi-threading changes
7685
-----------------------
7786

base/options.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ struct JLOptions
99
commands::Ptr{Ptr{UInt8}} # (e)eval, (E)print, (L)load
1010
image_file::Ptr{UInt8}
1111
cpu_target::Ptr{UInt8}
12+
nthreads::Int32
1213
nprocs::Int32
1314
machine_file::Ptr{UInt8}
1415
project::Ptr{UInt8}

doc/man/julia.1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ Evaluate <expr> and display the result
101101
-L, --load <file>
102102
Load <file> immediately on all processors
103103

104+
.TP
105+
-t, --threads <n>
106+
Enable n threads
107+
104108
.TP
105109
-p, --procs <n>
106110
Run n local processes

doc/src/manual/environment-variables.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ A [`Float64`](@ref) that sets the value of `Distributed.worker_timeout()` (defau
182182
This function gives the number of seconds a worker process will wait for
183183
a master process to establish a connection before dying.
184184

185-
### `JULIA_NUM_THREADS`
185+
### [`JULIA_NUM_THREADS`](@id JULIA_NUM_THREADS)
186186

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

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

198+
!!! compat "Julia 1.5"
199+
In Julia 1.5 and above the number of threads can also be specified on startup
200+
using the `-t`/`--threads` command line argument.
201+
198202
### `JULIA_THREAD_SLEEP_THRESHOLD`
199203

200204
If set to a string that starts with the case-insensitive substring `"infinite"`,

doc/src/manual/getting-started.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ julia [switches] -- [programfile] [args...]
109109
|`-e`, `--eval <expr>` |Evaluate `<expr>`|
110110
|`-E`, `--print <expr>` |Evaluate `<expr>` and display the result|
111111
|`-L`, `--load <file>` |Load `<file>` immediately on all processors|
112+
|`-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|
112113
|`-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)|
113114
|`--machine-file <file>` |Run processes on hosts listed in `<file>`|
114115
|`-i` |Interactive mode; REPL runs and `isinteractive()` is true|

doc/src/manual/parallel-computing.md

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -231,28 +231,21 @@ julia> Threads.nthreads()
231231
1
232232
```
233233

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

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

239-
```bash
240-
export JULIA_NUM_THREADS=4
241-
```
242-
243-
C shell on Linux/OSX, CMD on Windows:
243+
Lets start Julia with 4 threads:
244244

245245
```bash
246-
set JULIA_NUM_THREADS=4
246+
$ julia --threads 4
247247
```
248248

249-
Powershell on Windows:
250-
251-
```powershell
252-
$env:JULIA_NUM_THREADS=4
253-
```
254-
255-
256249
Let's verify there are 4 threads at our disposal.
257250

258251
```julia-repl
@@ -267,6 +260,29 @@ julia> Threads.threadid()
267260
1
268261
```
269262

263+
!!! note
264+
If you prefer to use the environment variable you can set it as follows in
265+
Bash (Linux/macOS):
266+
```bash
267+
export JULIA_NUM_THREADS=4
268+
```
269+
C shell on Linux/macOS, CMD on Windows:
270+
```bash
271+
set JULIA_NUM_THREADS=4
272+
```
273+
Powershell on Windows:
274+
```powershell
275+
$env:JULIA_NUM_THREADS=4
276+
```
277+
Note that this must be done *before* starting Julia.
278+
279+
!!! note
280+
The number of threads specified with `-t`/`--threads` is propagated to worker processes
281+
that are spawned using the `-p`/`--procs` or `--machine-file` command line options.
282+
For example, `julia -p2 -t2` spawns 1 main process with 2 worker processes, and all
283+
three processes have 2 threads enabled. For more fine grained control over worker
284+
threads use [`addprocs`](@ref) and pass `-t`/`--threads` as `exeflags`.
285+
270286
## The `@threads` Macro
271287

272288
Let's work a simple example using our native threads. Let us create an array of zeros:

src/jloptions.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ jl_options_t jl_options = { 0, // quiet
3535
NULL, // cmds
3636
NULL, // image_file (will be filled in below)
3737
NULL, // cpu_target ("native", "core2", etc...)
38+
0, // nthreads
3839
0, // nprocs
3940
NULL, // machine_file
4041
NULL, // project
@@ -97,6 +98,9 @@ static const char opts[] =
9798
" -L, --load <file> Load <file> immediately on all processors\n\n"
9899

99100
// parallel options
101+
" -t, --threads {N|auto} Enable N threads; \"auto\" currently sets N to the number of local\n"
102+
" CPU threads but this might change in the future\n"
103+
" -t, --threads {N|auto} Enable N threads. \"auto\" sets N to the number of local CPU threads.\n"
100104
" -p, --procs {N|auto} Integer value N launches N additional local worker processes\n"
101105
" \"auto\" launches as many workers as the number of local CPU threads (logical cores)\n"
102106
" --machine-file <file> Run processes on hosts listed in <file>\n\n"
@@ -190,7 +194,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
190194
opt_machine_file,
191195
opt_project,
192196
};
193-
static const char* const shortopts = "+vhqH:e:E:L:J:C:ip:O:g:";
197+
static const char* const shortopts = "+vhqH:e:E:L:J:C:it:p:O:g:";
194198
static const struct option longopts[] = {
195199
// exposed command line options
196200
// NOTE: This set of required arguments need to be kept in sync
@@ -209,6 +213,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
209213
{ "compiled-modules", required_argument, 0, opt_compiled_modules },
210214
{ "cpu-target", required_argument, 0, 'C' },
211215
{ "procs", required_argument, 0, 'p' },
216+
{ "threads", required_argument, 0, 't' },
212217
{ "machine-file", required_argument, 0, opt_machine_file },
213218
{ "project", optional_argument, 0, opt_project },
214219
{ "color", required_argument, 0, opt_color },
@@ -388,6 +393,18 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
388393
if (!jl_options.cpu_target)
389394
jl_error("julia: failed to allocate memory");
390395
break;
396+
case 't': // threads
397+
errno = 0;
398+
if (!strcmp(optarg,"auto")) {
399+
jl_options.nthreads = -1;
400+
}
401+
else {
402+
long nthreads = strtol(optarg, &endptr, 10);
403+
if (errno != 0 || optarg == endptr || *endptr != 0 || nthreads < 1 || nthreads >= INT_MAX)
404+
jl_errorf("julia: -t,--threads=<n> must be an integer >= 1");
405+
jl_options.nthreads = (int)nthreads;
406+
}
407+
break;
391408
case 'p': // procs
392409
errno = 0;
393410
if (!strcmp(optarg,"auto")) {

src/julia.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,6 +1900,7 @@ typedef struct {
19001900
const char **cmds;
19011901
const char *image_file;
19021902
const char *cpu_target;
1903+
int32_t nthreads;
19031904
int32_t nprocs;
19041905
const char *machine_file;
19051906
const char *project;

src/threading.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,11 @@ void jl_init_threading(void)
407407
// how many threads available, usable
408408
int max_threads = jl_cpu_threads();
409409
jl_n_threads = JULIA_NUM_THREADS;
410-
cp = getenv(NUM_THREADS_NAME);
411-
if (cp)
410+
if (jl_options.nthreads < 0) // --threads=auto
411+
jl_n_threads = max_threads;
412+
else if (jl_options.nthreads > 0) // --threads=N
413+
jl_n_threads = jl_options.nthreads;
414+
else if ((cp = getenv(NUM_THREADS_NAME)))
412415
jl_n_threads = (uint64_t)strtol(cp, NULL, 10);
413416
if (jl_n_threads > max_threads)
414417
jl_n_threads = max_threads;

stdlib/Distributed/src/cluster.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,7 @@ end
12981298

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

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

1314+
# Propagate --threads to workers
1315+
exeflags = opts.nthreads > 0 ? `--threads=$(opts.nthreads)` : ``
1316+
13131317
# add processors
13141318
if opts.nprocs > 0
1315-
addprocs(opts.nprocs)
1319+
addprocs(opts.nprocs; exeflags=exeflags)
13161320
end
13171321

13181322
# load processes from machine file
13191323
if opts.machine_file != C_NULL
1320-
addprocs(load_machine_file(unsafe_string(opts.machine_file)))
1324+
addprocs(load_machine_file(unsafe_string(opts.machine_file)); exeflags=exeflags)
13211325
end
13221326
return nothing
13231327
end

0 commit comments

Comments
 (0)