Skip to content

Commit e169fa8

Browse files
DaniPopeszerosnacks
authored andcommitted
feat: add global -j, --threads (foundry-rs#9367)
* feat: add global -j, --threads * Update crates/cli/src/opts/global.rs * fix tests after comment update --------- Co-authored-by: zerosnacks <[email protected]> Co-authored-by: zerosnacks <[email protected]>
1 parent 4e89b9a commit e169fa8

File tree

9 files changed

+62
-44
lines changed

9 files changed

+62
-44
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cast/bin/cmd/create2.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use eyre::{Result, WrapErr};
44
use rand::{rngs::StdRng, RngCore, SeedableRng};
55
use regex::RegexSetBuilder;
66
use std::{
7-
num::NonZeroUsize,
87
sync::{
98
atomic::{AtomicBool, Ordering},
109
Arc,
@@ -73,9 +72,9 @@ pub struct Create2Args {
7372
#[arg(alias = "ch", long, value_name = "HASH", required_unless_present = "init_code")]
7473
init_code_hash: Option<String>,
7574

76-
/// Number of threads to use. Defaults to and caps at the number of logical cores.
77-
#[arg(short, long)]
78-
jobs: Option<NonZeroUsize>,
75+
/// Number of threads to use. Specifying 0 defaults to the number of logical cores.
76+
#[arg(global = true, long, short = 'j', visible_alias = "jobs")]
77+
threads: Option<usize>,
7978

8079
/// Address of the caller. Used for the first 20 bytes of the salt.
8180
#[arg(long, value_name = "ADDRESS")]
@@ -107,7 +106,7 @@ impl Create2Args {
107106
salt,
108107
init_code,
109108
init_code_hash,
110-
jobs,
109+
threads,
111110
caller,
112111
seed,
113112
no_random,
@@ -168,8 +167,8 @@ impl Create2Args {
168167
let regex = RegexSetBuilder::new(regexs).case_insensitive(!case_sensitive).build()?;
169168

170169
let mut n_threads = std::thread::available_parallelism().map_or(1, |n| n.get());
171-
if let Some(jobs) = jobs {
172-
n_threads = n_threads.min(jobs.get());
170+
if let Some(threads) = threads {
171+
n_threads = n_threads.min(threads);
173172
}
174173
if cfg!(test) {
175174
n_threads = n_threads.min(2);
@@ -433,8 +432,14 @@ mod tests {
433432

434433
#[test]
435434
fn j0() {
436-
let e =
437-
Create2Args::try_parse_from(["foundry-cli", "--starts-with=00", "-j0"]).unwrap_err();
438-
let _ = e.print();
435+
let args = Create2Args::try_parse_from([
436+
"foundry-cli",
437+
"--starts-with=00",
438+
"--init-code-hash",
439+
&B256::ZERO.to_string(),
440+
"-j0",
441+
])
442+
.unwrap();
443+
assert_eq!(args.threads, Some(0));
439444
}
440445
}

crates/cast/tests/cli/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ Options:
2828
-h, --help
2929
Print help (see a summary with '-h')
3030
31+
-j, --threads <THREADS>
32+
Number of threads to use. Specifying 0 defaults to the number of logical cores
33+
34+
[aliases: jobs]
35+
3136
-V, --version
3237
Print version
3338

crates/cli/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,16 @@ dotenvy = "0.15"
3737
eyre.workspace = true
3838
futures.workspace = true
3939
indicatif = "0.17"
40+
rayon.workspace = true
4041
regex = { workspace = true, default-features = false }
42+
serde_json.workspace = true
4143
serde.workspace = true
4244
strsim = "0.11"
4345
strum = { workspace = true, features = ["derive"] }
4446
tokio = { workspace = true, features = ["macros"] }
4547
tracing-subscriber = { workspace = true, features = ["registry", "env-filter"] }
4648
tracing.workspace = true
4749
yansi.workspace = true
48-
serde_json.workspace = true
4950

5051
tracing-tracy = { version = "0.11", optional = true }
5152

crates/cli/src/opts/global.rs

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use foundry_common::shell::{ColorChoice, OutputFormat, OutputMode, Shell, Verbos
33
use serde::{Deserialize, Serialize};
44

55
/// Global options.
6-
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, Parser)]
6+
#[derive(Clone, Debug, Default, Serialize, Deserialize, Parser)]
77
pub struct GlobalOpts {
88
/// Verbosity level of the log messages.
99
///
@@ -16,39 +16,47 @@ pub struct GlobalOpts {
1616
/// - 3 (-vvv): Print execution traces for failing tests.
1717
/// - 4 (-vvvv): Print execution traces for all tests, and setup traces for failing tests.
1818
/// - 5 (-vvvvv): Print execution and setup traces for all tests.
19-
#[clap(short, long, global = true, verbatim_doc_comment, conflicts_with = "quiet", action = ArgAction::Count, help_heading = "Display options")]
20-
pub verbosity: Verbosity,
19+
#[arg(help_heading = "Display options", global = true, short, long, verbatim_doc_comment, conflicts_with = "quiet", action = ArgAction::Count)]
20+
verbosity: Verbosity,
2121

2222
/// Do not print log messages.
23-
#[clap(short, long, global = true, alias = "silent", help_heading = "Display options")]
23+
#[arg(help_heading = "Display options", global = true, short, long, alias = "silent")]
2424
quiet: bool,
2525

2626
/// Format log messages as JSON.
27-
#[clap(
28-
long,
29-
global = true,
30-
alias = "format-json",
31-
conflicts_with_all = &["quiet", "color"],
32-
help_heading = "Display options"
33-
)]
27+
#[arg(help_heading = "Display options", global = true, long, alias = "format-json", conflicts_with_all = &["quiet", "color"])]
3428
json: bool,
3529

3630
/// The color of the log messages.
37-
#[clap(long, global = true, value_enum, help_heading = "Display options")]
31+
#[arg(help_heading = "Display options", global = true, long, value_enum)]
3832
color: Option<ColorChoice>,
33+
34+
/// Number of threads to use. Specifying 0 defaults to the number of logical cores.
35+
#[arg(global = true, long, short = 'j', visible_alias = "jobs")]
36+
threads: Option<usize>,
3937
}
4038

4139
impl GlobalOpts {
4240
/// Initialize the global options.
43-
pub fn init(self) -> eyre::Result<()> {
41+
pub fn init(&self) -> eyre::Result<()> {
4442
// Set the global shell.
4543
self.shell().set();
4644

45+
// Initialize the thread pool only if `threads` was requested to avoid unnecessary overhead.
46+
if self.threads.is_some() {
47+
self.force_init_thread_pool()?;
48+
}
49+
4750
Ok(())
4851
}
4952

53+
/// Initialize the global thread pool.
54+
pub fn force_init_thread_pool(&self) -> eyre::Result<()> {
55+
init_thread_pool(self.threads.unwrap_or(0))
56+
}
57+
5058
/// Create a new shell instance.
51-
pub fn shell(self) -> Shell {
59+
pub fn shell(&self) -> Shell {
5260
let mode = match self.quiet {
5361
true => OutputMode::Quiet,
5462
false => OutputMode::Normal,
@@ -62,3 +70,12 @@ impl GlobalOpts {
6270
Shell::new_with(format, mode, color, self.verbosity)
6371
}
6472
}
73+
74+
/// Initialize the global thread pool.
75+
pub fn init_thread_pool(threads: usize) -> eyre::Result<()> {
76+
rayon::ThreadPoolBuilder::new()
77+
.thread_name(|i| format!("foundry-{i}"))
78+
.num_threads(threads)
79+
.build_global()?;
80+
Ok(())
81+
}

crates/config/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ no_match_contract = "Bar"
115115
match_path = "*/Foo*"
116116
no_match_path = "*/Bar*"
117117
no_match_coverage = "Baz"
118-
# Number of threads to use. Not set or zero specifies the number of logical cores.
118+
# Number of threads to use. Specifying 0 defaults to the number of logical cores.
119119
threads = 0
120120
# whether to show test execution progress
121121
show_progress = true

crates/forge/bin/cmd/test/mod.rs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ foundry_config::merge_impl_figment_convert!(TestArgs, opts, evm_opts);
6565
#[derive(Clone, Debug, Parser)]
6666
#[command(next_help_heading = "Test options")]
6767
pub struct TestArgs {
68-
/// Include the global options.
68+
// Include global options for users of this struct.
6969
#[command(flatten)]
7070
pub global: GlobalOpts,
7171

@@ -149,11 +149,6 @@ pub struct TestArgs {
149149
#[arg(long)]
150150
pub fuzz_input_file: Option<String>,
151151

152-
/// Max concurrent threads to use.
153-
/// Default value is the number of available CPUs.
154-
#[arg(long, short = 'j', visible_alias = "jobs")]
155-
pub threads: Option<usize>,
156-
157152
/// Show test execution progress.
158153
#[arg(long)]
159154
pub show_progress: bool,
@@ -276,13 +271,6 @@ impl TestArgs {
276271
// Merge all configs.
277272
let (mut config, mut evm_opts) = self.load_config_and_evm_opts_emit_warnings()?;
278273

279-
// Set number of max threads to execute tests.
280-
// If not specified then the number of threads determined by rayon will be used.
281-
if let Some(test_threads) = config.threads {
282-
trace!(target: "forge::test", "execute tests with {} max threads", test_threads);
283-
rayon::ThreadPoolBuilder::new().num_threads(test_threads).build_global()?;
284-
}
285-
286274
// Explicitly enable isolation for gas reports for more correct gas accounting.
287275
if self.gas_report {
288276
evm_opts.isolate = true;
@@ -898,10 +886,6 @@ impl Provider for TestArgs {
898886
dict.insert("show_progress".to_string(), true.into());
899887
}
900888

901-
if let Some(threads) = self.threads {
902-
dict.insert("threads".to_string(), threads.into());
903-
}
904-
905889
Ok(Map::from([(Config::selected_profile(), dict)]))
906890
}
907891
}

crates/forge/tests/cli/cmd.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ Options:
3333
-h, --help
3434
Print help (see a summary with '-h')
3535
36+
-j, --threads <THREADS>
37+
Number of threads to use. Specifying 0 defaults to the number of logical cores
38+
39+
[aliases: jobs]
40+
3641
-V, --version
3742
Print version
3843

crates/script/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ foundry_config::merge_impl_figment_convert!(ScriptArgs, opts, evm_opts);
7878
/// CLI arguments for `forge script`.
7979
#[derive(Clone, Debug, Default, Parser)]
8080
pub struct ScriptArgs {
81-
/// Include the global options.
81+
// Include global options for users of this struct.
8282
#[command(flatten)]
8383
pub global: GlobalOpts,
8484

0 commit comments

Comments
 (0)