Skip to content

Commit f536964

Browse files
committed
Make OpenMP thread/MPI proc configuration more flexible
1 parent 777dec5 commit f536964

5 files changed

Lines changed: 59 additions & 74 deletions

File tree

benchmarks/config.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99

1010
from .openmc_runner import OpenMCRunner
1111

12-
_THREAD_OPTIONS: Tuple[int, ...] = (1, 2)
13-
1412

1513
def _detect_mpi_runner() -> Optional[Tuple[str, ...]]:
1614
for candidate in ("mpirun", "mpiexec"):
@@ -32,7 +30,17 @@ def _detect_mpi_enabled() -> bool:
3230

3331

3432
_MPI_ENABLED = _detect_mpi_enabled()
35-
_MPI_OPTIONS: Tuple[Optional[int], ...] = (None,) if (_MPI_ENABLED and _MPI_RUNNER) else (None,)
33+
34+
# Explicit list of (threads, MPI procs) configurations to benchmark
35+
_CONFIGS: Tuple[Tuple[int, Optional[int]], ...] = (
36+
(1, None),
37+
(4, None),
38+
)
39+
if _MPI_ENABLED and _MPI_RUNNER:
40+
_CONFIGS += (
41+
(1, 4),
42+
(2, 2),
43+
)
3644

3745

3846
def _param_key(threads: object, mpi_procs: object) -> Tuple[int, Optional[int]]:
@@ -49,9 +57,8 @@ def _nan(value: Optional[float]) -> float:
4957

5058

5159
__all__ = [
52-
"_MPI_OPTIONS",
60+
"_CONFIGS",
5361
"_MPI_RUNNER",
54-
"_THREAD_OPTIONS",
5562
"_param_key",
5663
"_nan",
5764
]

benchmarks/models/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,14 @@ def _discover() -> None:
3131
if builder is None:
3232
continue
3333
benchmark_name = getattr(module, "BENCHMARK_NAME", _default_benchmark_name(module_info.name))
34-
thread_opts = getattr(module, "THREAD_OPTIONS", None)
35-
mpi_opts = getattr(module, "MPI_OPTIONS", None)
34+
configs = getattr(module, "CONFIGS", None)
3635
custom_metrics = getattr(module, "CUSTOM_METRICS", None)
37-
MODEL_REGISTRY[module_info.name] = (benchmark_name, builder, thread_opts, mpi_opts, custom_metrics)
36+
MODEL_REGISTRY[module_info.name] = (benchmark_name, builder, configs, custom_metrics)
3837

3938

4039
_discover()
4140

42-
for _module, (_benchmark_name, builder, _thread_opts, _mpi_opts, _custom) in MODEL_REGISTRY.items():
41+
for _module, (_benchmark_name, builder, _configs, _custom) in MODEL_REGISTRY.items():
4342
globals()[_module] = builder
4443

4544
__all__ = ['MODEL_REGISTRY', 'ModelBuilder', 'ModelSpec'] + sorted(MODEL_REGISTRY)

benchmarks/scripts/__init__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,14 @@ def _discover() -> None:
3636
if runner is None:
3737
continue
3838
benchmark_name = getattr(module, "BENCHMARK_NAME", _default_benchmark_name(module_info.name))
39-
thread_opts = getattr(module, "THREAD_OPTIONS", None)
40-
mpi_opts = getattr(module, "MPI_OPTIONS", None)
39+
configs = getattr(module, "CONFIGS", None)
4140
custom_metrics = getattr(module, "CUSTOM_METRICS", None)
4241
return_metrics = tuple(getattr(module, "RETURN_METRICS", ()))
4342
module_path = f"{package}.{module_info.name}"
4443
SCRIPT_REGISTRY[module_info.name] = (
4544
benchmark_name,
4645
module_path,
47-
thread_opts,
48-
mpi_opts,
46+
configs,
4947
custom_metrics,
5048
return_metrics,
5149
)

benchmarks/scripts/jetson2d_mgxs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from benchmarks.models._jetson2d import build_base_model
1313

1414
BENCHMARK_NAME = "Jetson2dMgxs"
15-
THREAD_OPTIONS = (1, 2, 4)
15+
CONFIGS = ((1, None), (2, None), (4, None))
1616

1717

1818
def run_benchmark(threads, mpi_procs):

benchmarks/suites/base.py

Lines changed: 41 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
import openmc
1212

1313
from ..openmc_runner import OpenMCRunResult, OpenMCRunner, _tty_write, _run_subprocess_live
14-
from ..config import _MPI_OPTIONS, _MPI_RUNNER, _THREAD_OPTIONS, _param_key, _nan
14+
from ..config import _CONFIGS, _MPI_RUNNER, _param_key, _nan
1515

1616

1717
def _make_custom_track(metric_name: str) -> Callable:
1818
"""Create a ``track_*`` method that reads a pre-computed custom metric."""
1919

20-
def track(self, results, threads, mpi_procs):
21-
result = results[_param_key(threads, mpi_procs)]
20+
def track(self, results, config):
21+
result = results[_param_key(*config)]
2222
return _nan(result.custom_metrics.get(metric_name))
2323

2424
track.__name__ = f"track_{metric_name}"
@@ -29,12 +29,11 @@ def track(self, results, threads, mpi_procs):
2929
class _BaseBenchmark:
3030
"""Common base for all benchmarks with ``time -v`` metrics."""
3131

32-
params = (_THREAD_OPTIONS, _MPI_OPTIONS)
33-
param_names = ("threads", "mpi_procs")
32+
params = (_CONFIGS,)
33+
param_names = ("config",)
3434
timeout = 600
3535

36-
thread_options: Tuple[int, ...] = _THREAD_OPTIONS
37-
mpi_options: Tuple[Optional[int], ...] = _MPI_OPTIONS
36+
configs: Tuple[Tuple[int, Optional[int]], ...] = _CONFIGS
3837

3938
_custom_metrics: Dict[str, Callable] = {}
4039

@@ -45,30 +44,27 @@ def _compute_custom_metrics(self, result: OpenMCRunResult) -> None:
4544
def track_elapsed_wall(
4645
self,
4746
results: Dict[Tuple[int, Optional[int]], OpenMCRunResult],
48-
threads: int,
49-
mpi_procs: Optional[int],
47+
config: Tuple[int, Optional[int]],
5048
) -> float:
51-
result = results[_param_key(threads, mpi_procs)]
49+
result = results[_param_key(*config)]
5250
return _nan(result.time_usage.elapsed_seconds)
5351
track_elapsed_wall.unit = "seconds"
5452

5553
def track_user_cpu(
5654
self,
5755
results: Dict[Tuple[int, Optional[int]], OpenMCRunResult],
58-
threads: int,
59-
mpi_procs: Optional[int],
56+
config: Tuple[int, Optional[int]],
6057
) -> float:
61-
result = results[_param_key(threads, mpi_procs)]
58+
result = results[_param_key(*config)]
6259
return _nan(result.time_usage.user_seconds)
6360
track_user_cpu.unit = "seconds"
6461

6562
def track_max_rss_kb(
6663
self,
6764
results: Dict[Tuple[int, Optional[int]], OpenMCRunResult],
68-
threads: int,
69-
mpi_procs: Optional[int],
65+
config: Tuple[int, Optional[int]],
7066
) -> float:
71-
result = results[_param_key(threads, mpi_procs)]
67+
result = results[_param_key(*config)]
7268
rss = result.time_usage.max_rss_kb
7369
return float(rss) if rss is not None else _nan(None)
7470
track_max_rss_kb.unit = "KB"
@@ -90,66 +86,60 @@ def setup_cache(self, *_params: object) -> Dict[Tuple[int, Optional[int]], OpenM
9086
runner = self._ensure_runner()
9187
model = self._ensure_model()
9288
cache: Dict[Tuple[int, Optional[int]], OpenMCRunResult] = {}
93-
for threads in self.thread_options:
94-
for mpi_procs in self.mpi_options:
95-
_tty_write(f" Running: threads={threads}, mpi_procs={mpi_procs}\n")
96-
result = self._run_model(runner, model, threads, mpi_procs)
97-
self._compute_custom_metrics(result)
98-
cache[(threads, mpi_procs)] = result
89+
for threads, mpi_procs in self.configs:
90+
_tty_write(f" Running: threads={threads}, mpi_procs={mpi_procs}\n")
91+
result = self._run_model(runner, model, threads, mpi_procs)
92+
self._compute_custom_metrics(result)
93+
cache[(threads, mpi_procs)] = result
9994
self._cache = cache
10095
return self._cache
10196

10297
def track_total_time_elapsed(
10398
self,
10499
results: Dict[Tuple[int, Optional[int]], OpenMCRunResult],
105-
threads: int,
106-
mpi_procs: Optional[int],
100+
config: Tuple[int, Optional[int]],
107101
) -> float:
108-
result = results[_param_key(threads, mpi_procs)]
102+
result = results[_param_key(*config)]
109103
stats = result.timing_stats
110104
return _nan(stats.total_elapsed if stats else None)
111105
track_total_time_elapsed.unit = "seconds"
112106

113107
def track_initialization_time(
114108
self,
115109
results: Dict[Tuple[int, Optional[int]], OpenMCRunResult],
116-
threads: int,
117-
mpi_procs: Optional[int],
110+
config: Tuple[int, Optional[int]],
118111
) -> float:
119-
result = results[_param_key(threads, mpi_procs)]
112+
result = results[_param_key(*config)]
120113
stats = result.timing_stats
121114
return _nan(stats.initialization if stats else None)
122115
track_initialization_time.unit = "seconds"
123116

124117
def track_transport_time(
125118
self,
126119
results: Dict[Tuple[int, Optional[int]], OpenMCRunResult],
127-
threads: int,
128-
mpi_procs: Optional[int],
120+
config: Tuple[int, Optional[int]],
129121
) -> float:
130-
result = results[_param_key(threads, mpi_procs)]
122+
result = results[_param_key(*config)]
131123
stats = result.timing_stats
132124
return _nan(stats.transport if stats else None)
133125
track_transport_time.unit = "seconds"
134126

135127
def track_calc_rate_inactive(
136128
self,
137129
results: Dict[Tuple[int, Optional[int]], OpenMCRunResult],
138-
threads: int,
139-
mpi_procs: Optional[int],
130+
config: Tuple[int, Optional[int]],
140131
) -> float:
141-
result = results[_param_key(threads, mpi_procs)]
132+
result = results[_param_key(*config)]
142133
stats = result.timing_stats
143134
return _nan(stats.calc_rate_inactive if stats else None)
144135
track_calc_rate_inactive.unit = "particles / second"
145136

146137
def track_calc_rate_active(
147138
self,
148139
results: Dict[Tuple[int, Optional[int]], OpenMCRunResult],
149-
threads: int,
150-
mpi_procs: Optional[int],
140+
config: Tuple[int, Optional[int]],
151141
) -> float:
152-
result = results[_param_key(threads, mpi_procs)]
142+
result = results[_param_key(*config)]
153143
stats = result.timing_stats
154144
return _nan(stats.calc_rate_active if stats else None)
155145
track_calc_rate_active.unit = "particles / second"
@@ -227,17 +217,14 @@ def make_benchmark(
227217
name: str,
228218
model_builder: Callable[[], openmc.Model],
229219
*,
230-
thread_options: Tuple[int, ...] | None = None,
231-
mpi_options: Tuple[Optional[int], ...] | None = None,
220+
configs: Tuple[Tuple[int, Optional[int]], ...] | None = None,
232221
custom_metrics: Dict[str, Callable] | None = None,
233222
) -> Type[_OpenMCModelBenchmark]:
234-
threads = thread_options or _THREAD_OPTIONS
235-
mpi = mpi_options or _MPI_OPTIONS
223+
config_options = configs or _CONFIGS
236224
namespace: Dict[str, object] = {
237225
"__doc__": f"Benchmark for {name} model.",
238-
"thread_options": threads,
239-
"mpi_options": mpi,
240-
"params": (threads, mpi),
226+
"configs": config_options,
227+
"params": (config_options,),
241228
"_build_model": staticmethod(model_builder),
242229
"_custom_metrics": custom_metrics or {},
243230
}
@@ -252,17 +239,15 @@ def make_benchmark(
252239
# Python script benchmarks
253240
# ---------------------------------------------------------------------------
254241

255-
_PYTHON_DEFAULT_THREADS: Tuple[int, ...] = (1,)
256-
_PYTHON_DEFAULT_MPI: Tuple[Optional[int], ...] = (None,)
242+
_PYTHON_DEFAULT_CONFIGS: Tuple[Tuple[int, Optional[int]], ...] = ((1, None),)
257243

258244

259245
class _PythonBenchmark(_BaseBenchmark):
260246
"""Benchmark that runs arbitrary Python code as a subprocess."""
261247

262-
params = (_PYTHON_DEFAULT_THREADS, _PYTHON_DEFAULT_MPI)
248+
params = (_PYTHON_DEFAULT_CONFIGS,)
263249

264-
thread_options: Tuple[int, ...] = _PYTHON_DEFAULT_THREADS
265-
mpi_options: Tuple[Optional[int], ...] = _PYTHON_DEFAULT_MPI
250+
configs: Tuple[Tuple[int, Optional[int]], ...] = _PYTHON_DEFAULT_CONFIGS
266251

267252
_module_path: str = "" # fully qualified module name, set by factory
268253
_return_metrics: Tuple[str, ...] = ()
@@ -276,10 +261,9 @@ def setup_cache(self, *_params: object) -> Dict[Tuple[int, Optional[int]], OpenM
276261
_tty_write(f"{'=' * 60}\n")
277262
if self._cache is None:
278263
cache: Dict[Tuple[int, Optional[int]], OpenMCRunResult] = {}
279-
for threads in self.thread_options:
280-
for mpi_procs in self.mpi_options:
281-
_tty_write(f" Running: threads={threads}, mpi_procs={mpi_procs}\n")
282-
cache[(threads, mpi_procs)] = self._run_script(threads, mpi_procs)
264+
for threads, mpi_procs in self.configs:
265+
_tty_write(f" Running: threads={threads}, mpi_procs={mpi_procs}\n")
266+
cache[(threads, mpi_procs)] = self._run_script(threads, mpi_procs)
283267
self._cache = cache
284268
return self._cache
285269

@@ -408,13 +392,11 @@ def make_python_benchmark(
408392
name: str,
409393
module_path: str,
410394
*,
411-
thread_options: Tuple[int, ...] | None = None,
412-
mpi_options: Tuple[Optional[int], ...] | None = None,
395+
configs: Tuple[Tuple[int, Optional[int]], ...] | None = None,
413396
custom_metrics: Dict[str, Callable] | None = None,
414397
return_metrics: Tuple[str, ...] | None = None,
415398
) -> Type[_PythonBenchmark]:
416-
threads = thread_options or _PYTHON_DEFAULT_THREADS
417-
mpi = mpi_options or _PYTHON_DEFAULT_MPI
399+
config_options = configs or _PYTHON_DEFAULT_CONFIGS
418400
return_metric_names = tuple(return_metrics or ())
419401
custom_metric_names = tuple((custom_metrics or {}).keys())
420402
duplicate_metrics = set(return_metric_names).intersection(custom_metric_names)
@@ -423,9 +405,8 @@ def make_python_benchmark(
423405
raise ValueError(f"Duplicate Python benchmark metric names: {names}")
424406
namespace: Dict[str, object] = {
425407
"__doc__": f"Python benchmark for {name}.",
426-
"thread_options": threads,
427-
"mpi_options": mpi,
428-
"params": (threads, mpi),
408+
"configs": config_options,
409+
"params": (config_options,),
429410
"_module_path": module_path,
430411
"_custom_metrics": custom_metrics or {},
431412
"_return_metrics": return_metric_names,

0 commit comments

Comments
 (0)