@@ -43,7 +43,7 @@ pub enum SimulationTool {
4343/// Run-level configuration owned by the orchestrator.
4444///
4545/// Holds all parameters that are constant across benchmark targets within a run,
46- /// plus the target(s) to execute.
46+ /// plus the list of targets to execute.
4747/// Constructed from CLI arguments and passed to [`Orchestrator::new`].
4848/// Use [`OrchestratorConfig::executor_config_for_command`] to produce a per-execution [`ExecutorConfig`].
4949#[ derive( Debug , Clone ) ]
@@ -53,7 +53,7 @@ pub struct OrchestratorConfig {
5353 pub repository_override : Option < RepositoryOverride > ,
5454 pub working_directory : Option < String > ,
5555
56- pub target : BenchmarkTarget ,
56+ pub targets : Vec < BenchmarkTarget > ,
5757
5858 pub modes : Vec < RunnerMode > ,
5959 pub instruments : Instruments ,
@@ -132,6 +132,25 @@ impl OrchestratorConfig {
132132 self . token = token;
133133 }
134134
135+ /// Compute the total number of executor runs that will be performed.
136+ ///
137+ /// All `Exec` targets are combined into a single invocation, while each
138+ /// `Entrypoint` target runs independently. Both are multiplied by the
139+ /// number of configured modes.
140+ pub fn expected_run_parts_count ( & self ) -> u32 {
141+ let has_exec = self
142+ . targets
143+ . iter ( )
144+ . any ( |t| matches ! ( t, BenchmarkTarget :: Exec { .. } ) ) ;
145+ let entrypoint_count = self
146+ . targets
147+ . iter ( )
148+ . filter ( |t| matches ! ( t, BenchmarkTarget :: Entrypoint { .. } ) )
149+ . count ( ) ;
150+ let invocation_count = ( if has_exec { 1 } else { 0 } ) + entrypoint_count;
151+ ( invocation_count * self . modes . len ( ) ) as u32
152+ }
153+
135154 /// Produce a per-execution [`ExecutorConfig`] for the given command and mode.
136155 pub fn executor_config_for_command ( & self , command : String ) -> ExecutorConfig {
137156 ExecutorConfig {
@@ -166,10 +185,10 @@ impl OrchestratorConfig {
166185 token : None ,
167186 repository_override : None ,
168187 working_directory : None ,
169- target : BenchmarkTarget :: Entrypoint {
188+ targets : vec ! [ BenchmarkTarget :: Entrypoint {
170189 command: String :: new( ) ,
171190 name: None ,
172- } ,
191+ } ] ,
173192 modes : vec ! [ RunnerMode :: Simulation ] ,
174193 instruments : Instruments :: test ( ) ,
175194 perf_unwinding_mode : None ,
@@ -197,6 +216,109 @@ impl ExecutorConfig {
197216mod tests {
198217 use super :: * ;
199218
219+ #[ test]
220+ fn test_expected_run_parts_count ( ) {
221+ use crate :: runner_mode:: RunnerMode ;
222+
223+ let base = OrchestratorConfig :: test ( ) ;
224+
225+ // Single entrypoint, single mode → 1
226+ let config = OrchestratorConfig {
227+ targets : vec ! [ BenchmarkTarget :: Entrypoint {
228+ command: "cmd" . into( ) ,
229+ name: None ,
230+ } ] ,
231+ modes : vec ! [ RunnerMode :: Simulation ] ,
232+ ..base. clone ( )
233+ } ;
234+ assert_eq ! ( config. expected_run_parts_count( ) , 1 ) ;
235+
236+ // Two entrypoints, single mode → 2
237+ let config = OrchestratorConfig {
238+ targets : vec ! [
239+ BenchmarkTarget :: Entrypoint {
240+ command: "cmd1" . into( ) ,
241+ name: None ,
242+ } ,
243+ BenchmarkTarget :: Entrypoint {
244+ command: "cmd2" . into( ) ,
245+ name: None ,
246+ } ,
247+ ] ,
248+ modes : vec ! [ RunnerMode :: Simulation ] ,
249+ ..base. clone ( )
250+ } ;
251+ assert_eq ! ( config. expected_run_parts_count( ) , 2 ) ;
252+
253+ // Multiple exec targets count as one invocation, single mode → 1
254+ let config = OrchestratorConfig {
255+ targets : vec ! [
256+ BenchmarkTarget :: Exec {
257+ command: vec![ "exec1" . into( ) ] ,
258+ name: None ,
259+ walltime_args: Default :: default ( ) ,
260+ } ,
261+ BenchmarkTarget :: Exec {
262+ command: vec![ "exec2" . into( ) ] ,
263+ name: None ,
264+ walltime_args: Default :: default ( ) ,
265+ } ,
266+ ] ,
267+ modes : vec ! [ RunnerMode :: Simulation ] ,
268+ ..base. clone ( )
269+ } ;
270+ assert_eq ! ( config. expected_run_parts_count( ) , 1 ) ;
271+
272+ // Mix of exec and entrypoint, single mode → 2
273+ let config = OrchestratorConfig {
274+ targets : vec ! [
275+ BenchmarkTarget :: Exec {
276+ command: vec![ "exec1" . into( ) ] ,
277+ name: None ,
278+ walltime_args: Default :: default ( ) ,
279+ } ,
280+ BenchmarkTarget :: Entrypoint {
281+ command: "cmd" . into( ) ,
282+ name: None ,
283+ } ,
284+ ] ,
285+ modes : vec ! [ RunnerMode :: Simulation ] ,
286+ ..base. clone ( )
287+ } ;
288+ assert_eq ! ( config. expected_run_parts_count( ) , 2 ) ;
289+
290+ // Single entrypoint, two modes → 2
291+ #[ allow( deprecated) ]
292+ let config = OrchestratorConfig {
293+ targets : vec ! [ BenchmarkTarget :: Entrypoint {
294+ command: "cmd" . into( ) ,
295+ name: None ,
296+ } ] ,
297+ modes : vec ! [ RunnerMode :: Simulation , RunnerMode :: Walltime ] ,
298+ ..base. clone ( )
299+ } ;
300+ assert_eq ! ( config. expected_run_parts_count( ) , 2 ) ;
301+
302+ // Mix of exec and entrypoint, two modes → 4
303+ #[ allow( deprecated) ]
304+ let config = OrchestratorConfig {
305+ targets : vec ! [
306+ BenchmarkTarget :: Exec {
307+ command: vec![ "exec1" . into( ) ] ,
308+ name: None ,
309+ walltime_args: Default :: default ( ) ,
310+ } ,
311+ BenchmarkTarget :: Entrypoint {
312+ command: "cmd" . into( ) ,
313+ name: None ,
314+ } ,
315+ ] ,
316+ modes : vec ! [ RunnerMode :: Simulation , RunnerMode :: Walltime ] ,
317+ ..base. clone ( )
318+ } ;
319+ assert_eq ! ( config. expected_run_parts_count( ) , 4 ) ;
320+ }
321+
200322 #[ test]
201323 fn test_repository_override_from_arg ( ) {
202324 let override_result =
0 commit comments