k4bench 0.0.13__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- k4bench-0.0.13/.codespellrc +7 -0
- k4bench-0.0.13/.devcontainer/devcontainer.json +37 -0
- k4bench-0.0.13/.github/actionlint.yaml +3 -0
- k4bench-0.0.13/.github/benchmarks/ALLEGRO_o1_v02.yml +48 -0
- k4bench-0.0.13/.github/benchmarks/ALLEGRO_o1_v03.yml +48 -0
- k4bench-0.0.13/.github/benchmarks/ALLEGRO_o2_v01.yml +48 -0
- k4bench-0.0.13/.github/benchmarks/IDEA_o1_v03.yml +50 -0
- k4bench-0.0.13/.github/benchmarks/IDEA_o2_v01.yml +50 -0
- k4bench-0.0.13/.github/scripts/list_benchmarks.py +120 -0
- k4bench-0.0.13/.github/scripts/machine_info.py +202 -0
- k4bench-0.0.13/.github/scripts/nightly_benchmark.sh +240 -0
- k4bench-0.0.13/.github/workflows/benchmark-detector.yml +112 -0
- k4bench-0.0.13/.github/workflows/ci.yml +82 -0
- k4bench-0.0.13/.github/workflows/deploy-dashboard.yml +111 -0
- k4bench-0.0.13/.github/workflows/docs.yml +52 -0
- k4bench-0.0.13/.github/workflows/nightly.yml +43 -0
- k4bench-0.0.13/.github/workflows/on-release-main.yml +31 -0
- k4bench-0.0.13/.gitignore +28 -0
- k4bench-0.0.13/.pre-commit-config.yaml +23 -0
- k4bench-0.0.13/CODE_OF_CONDUCT.md +133 -0
- k4bench-0.0.13/LICENSE +21 -0
- k4bench-0.0.13/PKG-INFO +115 -0
- k4bench-0.0.13/README.md +100 -0
- k4bench-0.0.13/codecov.yaml +12 -0
- k4bench-0.0.13/dashboard/Dockerfile +31 -0
- k4bench-0.0.13/dashboard/app.py +405 -0
- k4bench-0.0.13/dashboard/config.py +31 -0
- k4bench-0.0.13/dashboard/data.py +343 -0
- k4bench-0.0.13/dashboard/remote.py +284 -0
- k4bench-0.0.13/dashboard/remote_cache.py +71 -0
- k4bench-0.0.13/dashboard/requirements.txt +3 -0
- k4bench-0.0.13/dashboard/stats.py +102 -0
- k4bench-0.0.13/dashboard/tabs/__init__.py +0 -0
- k4bench-0.0.13/dashboard/tabs/event_memory.py +163 -0
- k4bench-0.0.13/dashboard/tabs/event_timing.py +161 -0
- k4bench-0.0.13/dashboard/tabs/impact.py +229 -0
- k4bench-0.0.13/dashboard/tabs/machine_info.py +246 -0
- k4bench-0.0.13/dashboard/tabs/region_timing/__init__.py +65 -0
- k4bench-0.0.13/dashboard/tabs/region_timing/_common.py +34 -0
- k4bench-0.0.13/dashboard/tabs/region_timing/attribution.py +273 -0
- k4bench-0.0.13/dashboard/tabs/region_timing/current_run.py +51 -0
- k4bench-0.0.13/dashboard/tabs/region_timing/historical.py +204 -0
- k4bench-0.0.13/dashboard/tabs/region_timing/step_analysis.py +260 -0
- k4bench-0.0.13/dashboard/tabs/trends.py +198 -0
- k4bench-0.0.13/dashboard/trend_window.py +44 -0
- k4bench-0.0.13/dashboard/ui_chrome.py +198 -0
- k4bench-0.0.13/dashboard/ui_utils.py +329 -0
- k4bench-0.0.13/docs/architecture/component-diagrams.md +79 -0
- k4bench-0.0.13/docs/architecture/data-flow.md +84 -0
- k4bench-0.0.13/docs/architecture/overview.md +143 -0
- k4bench-0.0.13/docs/developer-guide/development-setup.md +109 -0
- k4bench-0.0.13/docs/developer-guide/repository-layout.md +104 -0
- k4bench-0.0.13/docs/examples/advanced-workflows.md +138 -0
- k4bench-0.0.13/docs/examples/common-workflows.md +123 -0
- k4bench-0.0.13/docs/faq.md +51 -0
- k4bench-0.0.13/docs/gen_ref_pages.py +56 -0
- k4bench-0.0.13/docs/getting-started/first-workflow.md +165 -0
- k4bench-0.0.13/docs/getting-started/installation.md +126 -0
- k4bench-0.0.13/docs/getting-started/quickstart.md +130 -0
- k4bench-0.0.13/docs/glossary.md +129 -0
- k4bench-0.0.13/docs/index.md +134 -0
- k4bench-0.0.13/docs/reference/api-reference.md +62 -0
- k4bench-0.0.13/docs/reference/configuration-reference.md +84 -0
- k4bench-0.0.13/docs/reference/file-formats.md +117 -0
- k4bench-0.0.13/docs/requirements.txt +16 -0
- k4bench-0.0.13/docs/user-guide/commands.md +168 -0
- k4bench-0.0.13/docs/user-guide/configuration.md +149 -0
- k4bench-0.0.13/docs/user-guide/features/analysis.md +148 -0
- k4bench-0.0.13/docs/user-guide/features/dashboard.md +120 -0
- k4bench-0.0.13/docs/user-guide/features/geometry-patching.md +142 -0
- k4bench-0.0.13/docs/user-guide/features/sweep-modes.md +94 -0
- k4bench-0.0.13/docs/user-guide/features/timing-plugins.md +158 -0
- k4bench-0.0.13/docs/user-guide/overview.md +146 -0
- k4bench-0.0.13/k4bench/__init__.py +8 -0
- k4bench-0.0.13/k4bench/analysis/__init__.py +19 -0
- k4bench-0.0.13/k4bench/analysis/loader.py +258 -0
- k4bench-0.0.13/k4bench/analysis/plots/__init__.py +28 -0
- k4bench-0.0.13/k4bench/analysis/plots/_theme.py +46 -0
- k4bench-0.0.13/k4bench/analysis/plots/_traces.py +90 -0
- k4bench-0.0.13/k4bench/analysis/plots/_utils.py +196 -0
- k4bench-0.0.13/k4bench/analysis/plots/event.py +512 -0
- k4bench-0.0.13/k4bench/analysis/plots/overview.py +199 -0
- k4bench-0.0.13/k4bench/analysis/plots/region.py +419 -0
- k4bench-0.0.13/k4bench/benchmark/__init__.py +0 -0
- k4bench-0.0.13/k4bench/benchmark/ddsim.py +337 -0
- k4bench-0.0.13/k4bench/cli.py +237 -0
- k4bench-0.0.13/k4bench/geometry/__init__.py +0 -0
- k4bench-0.0.13/k4bench/geometry/patcher.py +406 -0
- k4bench-0.0.13/k4bench/geometry/scanner.py +135 -0
- k4bench-0.0.13/k4bench/plugin/__init__.py +0 -0
- k4bench-0.0.13/k4bench/plugin/runtime.py +144 -0
- k4bench-0.0.13/k4bench/results/__init__.py +0 -0
- k4bench-0.0.13/k4bench/results/model.py +103 -0
- k4bench-0.0.13/k4bench/results/reporter.py +73 -0
- k4bench-0.0.13/k4bench/runner/__init__.py +0 -0
- k4bench-0.0.13/k4bench/runner/executor.py +282 -0
- k4bench-0.0.13/k4bench/runner/parser.py +119 -0
- k4bench-0.0.13/k4bench.egg-info/PKG-INFO +115 -0
- k4bench-0.0.13/k4bench.egg-info/SOURCES.txt +135 -0
- k4bench-0.0.13/k4bench.egg-info/dependency_links.txt +1 -0
- k4bench-0.0.13/k4bench.egg-info/entry_points.txt +2 -0
- k4bench-0.0.13/k4bench.egg-info/requires.txt +5 -0
- k4bench-0.0.13/k4bench.egg-info/top_level.txt +1 -0
- k4bench-0.0.13/mkdocs.yml +156 -0
- k4bench-0.0.13/openshift/deployment.yaml +49 -0
- k4bench-0.0.13/openshift/route.yaml +17 -0
- k4bench-0.0.13/openshift/service.yaml +15 -0
- k4bench-0.0.13/openshift/userprovideddirectory.yaml +21 -0
- k4bench-0.0.13/plugin/CMakeLists.txt +68 -0
- k4bench-0.0.13/plugin/build.sh +58 -0
- k4bench-0.0.13/plugin/k4BenchRegionTimingAction.cpp +1280 -0
- k4bench-0.0.13/plugin/k4BenchTimingAction.cpp +210 -0
- k4bench-0.0.13/pyproject.toml +48 -0
- k4bench-0.0.13/requirements.txt +2 -0
- k4bench-0.0.13/setup.cfg +4 -0
- k4bench-0.0.13/setup.sh +81 -0
- k4bench-0.0.13/tests/conftest.py +2 -0
- k4bench-0.0.13/tests/fixtures/minimal_geometry/materials.xml +11 -0
- k4bench-0.0.13/tests/fixtures/minimal_geometry/minimal.xml +9 -0
- k4bench-0.0.13/tests/fixtures/minimal_geometry/subdetectors/calorimeter.xml +11 -0
- k4bench-0.0.13/tests/fixtures/minimal_geometry/subdetectors/tracker.xml +11 -0
- k4bench-0.0.13/tests/fixtures/time_output.txt +23 -0
- k4bench-0.0.13/tests/integration/test_plugin_build.py +55 -0
- k4bench-0.0.13/tests/integration/test_simulation.py +151 -0
- k4bench-0.0.13/tests/unit/test_analysis_loader.py +331 -0
- k4bench-0.0.13/tests/unit/test_analysis_plots.py +452 -0
- k4bench-0.0.13/tests/unit/test_benchmark.py +246 -0
- k4bench-0.0.13/tests/unit/test_cli.py +169 -0
- k4bench-0.0.13/tests/unit/test_dashboard_trends.py +69 -0
- k4bench-0.0.13/tests/unit/test_executor.py +220 -0
- k4bench-0.0.13/tests/unit/test_parser.py +156 -0
- k4bench-0.0.13/tests/unit/test_patcher.py +212 -0
- k4bench-0.0.13/tests/unit/test_plugin_runtime.py +398 -0
- k4bench-0.0.13/tests/unit/test_remote.py +223 -0
- k4bench-0.0.13/tests/unit/test_reporter.py +76 -0
- k4bench-0.0.13/tests/unit/test_scanner.py +177 -0
- k4bench-0.0.13/tests/unit/test_trend_window.py +65 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Devcontainer for local development
|
|
2
|
+
// See https://github.com/key4hep/key4hep-images/ for available images
|
|
3
|
+
// Requires the host have
|
|
4
|
+
// a) CernVM-FS installed (see https://cvmfs.readthedocs.io/en/stable/cpt-quickstart.html for instructions)
|
|
5
|
+
// b) The necessary repositories mounted on the host machine; sudo mount -t cvmfs sw.hsf.org /cvmfs/sw.hsf.org
|
|
6
|
+
{
|
|
7
|
+
"name": "Key4HEP Dev",
|
|
8
|
+
"image": "ghcr.io/key4hep/key4hep-images/alma9-cvmfs:latest",
|
|
9
|
+
|
|
10
|
+
// Make sure CVMFS is mounted on the host (and bind-mounted):
|
|
11
|
+
"mounts": [
|
|
12
|
+
"source=/cvmfs,target=/cvmfs,type=bind"
|
|
13
|
+
],
|
|
14
|
+
|
|
15
|
+
// "postCreateCommand" runs once after the container is created.
|
|
16
|
+
// Here, we append the source line to ~/.bashrc so new terminals pick it up.
|
|
17
|
+
"postCreateCommand": [
|
|
18
|
+
"bash",
|
|
19
|
+
"-c",
|
|
20
|
+
"echo 'source setup.sh' >> ~/.bashrc"
|
|
21
|
+
],
|
|
22
|
+
// Install and activate vs code extensions for environment
|
|
23
|
+
"customizations": {
|
|
24
|
+
"vscode": {
|
|
25
|
+
"extensions": [
|
|
26
|
+
"ms-python.python",
|
|
27
|
+
"ms-toolsai.jupyter",
|
|
28
|
+
"ms-vscode.cpptools",
|
|
29
|
+
"llvm-vs-code-extensions.vscode-clangd",
|
|
30
|
+
"ms-vscode.cmake-tools",
|
|
31
|
+
"ibm.output-colorizer",
|
|
32
|
+
"charliermarsh.ruff",
|
|
33
|
+
"eamodio.gitlens"
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# k4Bench nightly benchmark config — ALLEGRO o1 v02
|
|
2
|
+
#
|
|
3
|
+
# The nightly workflow expands every file in .github/benchmarks/ into a matrix
|
|
4
|
+
# of (detector × sample) jobs and uploads each result to EOS under:
|
|
5
|
+
# {detector}/{platform}/key4hep-{release}/{sample}/{date}/
|
|
6
|
+
# To add a new detector, drop a new <detector>.yml file in this directory.
|
|
7
|
+
# To add a new sample, append an entry to samples: below.
|
|
8
|
+
#
|
|
9
|
+
# ── Top-level keys (apply to every sample in this file) ────────────────────
|
|
10
|
+
# xml required detector geometry, $K4GEO-relative or absolute path
|
|
11
|
+
# verbose optional stream ddsim output to stdout (default: false)
|
|
12
|
+
# sweep optional baseline + one run per subdetector dropped
|
|
13
|
+
# include_only optional keep only these subdetectors (list, single run)
|
|
14
|
+
# exclude_only optional drop these subdetectors (list, single run)
|
|
15
|
+
# ddsim_args optional ddsim flags applied to every sample
|
|
16
|
+
# steering_file optional ddsim --steeringFile path; $VAR refs (e.g. $FCCCONFIG)
|
|
17
|
+
# are expanded inside the runner. Steering files for FCC
|
|
18
|
+
# detectors live in https://github.com/HEP-FCC/FCC-config
|
|
19
|
+
# and are installed under $FCCCONFIG by Key4hep, e.g.
|
|
20
|
+
# $FCCCONFIG/FullSim/IDEA/IDEA_o1_v03/SteeringFile_IDEA_o1_v03.py
|
|
21
|
+
# samples required list of sample entries — see below
|
|
22
|
+
#
|
|
23
|
+
# ── Per-sample keys (under samples:) ───────────────────────────────────────
|
|
24
|
+
# name required slug — becomes the EOS sample directory + job label
|
|
25
|
+
# n_events required positive integer
|
|
26
|
+
# ddsim_args optional appended to top-level ddsim_args (not replaced)
|
|
27
|
+
# input_files optional HepMC path(s); mutually exclusive with --enableGun
|
|
28
|
+
# steering_file optional overrides the top-level steering_file for this sample
|
|
29
|
+
#
|
|
30
|
+
# Sample-level keys override top-level, except ddsim_args (which concatenates).
|
|
31
|
+
# sweep / include_only / exclude_only are mutually exclusive.
|
|
32
|
+
|
|
33
|
+
xml: FCCee/ALLEGRO/compact/ALLEGRO_o1_v02/ALLEGRO_o1_v02.xml
|
|
34
|
+
verbose: false
|
|
35
|
+
sweep: true
|
|
36
|
+
|
|
37
|
+
samples:
|
|
38
|
+
- name: single_e-_10GeV
|
|
39
|
+
n_events: 500
|
|
40
|
+
ddsim_args: >-
|
|
41
|
+
--enableGun
|
|
42
|
+
--gun.particle e-
|
|
43
|
+
--gun.distribution uniform
|
|
44
|
+
--gun.energy 10*GeV
|
|
45
|
+
|
|
46
|
+
- name: p8_ee_Zbb_ecm91
|
|
47
|
+
n_events: 100
|
|
48
|
+
input_files: root://eospublic.cern.ch//eos/experiment/fcc/ee/generation/hepmc/p8_ee_Zbb_ecm91/events_noVtxSmear.hepmc
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# k4Bench nightly benchmark config — ALLEGRO o1 v03
|
|
2
|
+
#
|
|
3
|
+
# The nightly workflow expands every file in .github/benchmarks/ into a matrix
|
|
4
|
+
# of (detector × sample) jobs and uploads each result to EOS under:
|
|
5
|
+
# {detector}/{platform}/key4hep-{release}/{sample}/{date}/
|
|
6
|
+
# To add a new detector, drop a new <detector>.yml file in this directory.
|
|
7
|
+
# To add a new sample, append an entry to samples: below.
|
|
8
|
+
#
|
|
9
|
+
# ── Top-level keys (apply to every sample in this file) ────────────────────
|
|
10
|
+
# xml required detector geometry, $K4GEO-relative or absolute path
|
|
11
|
+
# verbose optional stream ddsim output to stdout (default: false)
|
|
12
|
+
# sweep optional baseline + one run per subdetector dropped
|
|
13
|
+
# include_only optional keep only these subdetectors (list, single run)
|
|
14
|
+
# exclude_only optional drop these subdetectors (list, single run)
|
|
15
|
+
# ddsim_args optional ddsim flags applied to every sample
|
|
16
|
+
# steering_file optional ddsim --steeringFile path; $VAR refs (e.g. $FCCCONFIG)
|
|
17
|
+
# are expanded inside the runner. Steering files for FCC
|
|
18
|
+
# detectors live in https://github.com/HEP-FCC/FCC-config
|
|
19
|
+
# and are installed under $FCCCONFIG by Key4hep, e.g.
|
|
20
|
+
# $FCCCONFIG/FullSim/IDEA/IDEA_o1_v03/SteeringFile_IDEA_o1_v03.py
|
|
21
|
+
# samples required list of sample entries — see below
|
|
22
|
+
#
|
|
23
|
+
# ── Per-sample keys (under samples:) ───────────────────────────────────────
|
|
24
|
+
# name required slug — becomes the EOS sample directory + job label
|
|
25
|
+
# n_events required positive integer
|
|
26
|
+
# ddsim_args optional appended to top-level ddsim_args (not replaced)
|
|
27
|
+
# input_files optional HepMC path(s); mutually exclusive with --enableGun
|
|
28
|
+
# steering_file optional overrides the top-level steering_file for this sample
|
|
29
|
+
#
|
|
30
|
+
# Sample-level keys override top-level, except ddsim_args (which concatenates).
|
|
31
|
+
# sweep / include_only / exclude_only are mutually exclusive.
|
|
32
|
+
|
|
33
|
+
xml: FCCee/ALLEGRO/compact/ALLEGRO_o1_v03/ALLEGRO_o1_v03.xml
|
|
34
|
+
verbose: false
|
|
35
|
+
sweep: true
|
|
36
|
+
|
|
37
|
+
samples:
|
|
38
|
+
- name: single_e-_10GeV
|
|
39
|
+
n_events: 500
|
|
40
|
+
ddsim_args: >-
|
|
41
|
+
--enableGun
|
|
42
|
+
--gun.particle e-
|
|
43
|
+
--gun.distribution uniform
|
|
44
|
+
--gun.energy 10*GeV
|
|
45
|
+
|
|
46
|
+
- name: p8_ee_Zbb_ecm91
|
|
47
|
+
n_events: 100
|
|
48
|
+
input_files: root://eospublic.cern.ch//eos/experiment/fcc/ee/generation/hepmc/p8_ee_Zbb_ecm91/events_noVtxSmear.hepmc
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# k4Bench nightly benchmark config — ALLEGRO o2 v01
|
|
2
|
+
#
|
|
3
|
+
# The nightly workflow expands every file in .github/benchmarks/ into a matrix
|
|
4
|
+
# of (detector × sample) jobs and uploads each result to EOS under:
|
|
5
|
+
# {detector}/{platform}/key4hep-{release}/{sample}/{date}/
|
|
6
|
+
# To add a new detector, drop a new <detector>.yml file in this directory.
|
|
7
|
+
# To add a new sample, append an entry to samples: below.
|
|
8
|
+
#
|
|
9
|
+
# ── Top-level keys (apply to every sample in this file) ────────────────────
|
|
10
|
+
# xml required detector geometry, $K4GEO-relative or absolute path
|
|
11
|
+
# verbose optional stream ddsim output to stdout (default: false)
|
|
12
|
+
# sweep optional baseline + one run per subdetector dropped
|
|
13
|
+
# include_only optional keep only these subdetectors (list, single run)
|
|
14
|
+
# exclude_only optional drop these subdetectors (list, single run)
|
|
15
|
+
# ddsim_args optional ddsim flags applied to every sample
|
|
16
|
+
# steering_file optional ddsim --steeringFile path; $VAR refs (e.g. $FCCCONFIG)
|
|
17
|
+
# are expanded inside the runner. Steering files for FCC
|
|
18
|
+
# detectors live in https://github.com/HEP-FCC/FCC-config
|
|
19
|
+
# and are installed under $FCCCONFIG by Key4hep, e.g.
|
|
20
|
+
# $FCCCONFIG/FullSim/IDEA/IDEA_o1_v03/SteeringFile_IDEA_o1_v03.py
|
|
21
|
+
# samples required list of sample entries — see below
|
|
22
|
+
#
|
|
23
|
+
# ── Per-sample keys (under samples:) ───────────────────────────────────────
|
|
24
|
+
# name required slug — becomes the EOS sample directory + job label
|
|
25
|
+
# n_events required positive integer
|
|
26
|
+
# ddsim_args optional appended to top-level ddsim_args (not replaced)
|
|
27
|
+
# input_files optional HepMC path(s); mutually exclusive with --enableGun
|
|
28
|
+
# steering_file optional overrides the top-level steering_file for this sample
|
|
29
|
+
#
|
|
30
|
+
# Sample-level keys override top-level, except ddsim_args (which concatenates).
|
|
31
|
+
# sweep / include_only / exclude_only are mutually exclusive.
|
|
32
|
+
|
|
33
|
+
xml: FCCee/ALLEGRO/compact/ALLEGRO_o2_v01/ALLEGRO_o2_v01.xml
|
|
34
|
+
verbose: false
|
|
35
|
+
sweep: true
|
|
36
|
+
|
|
37
|
+
samples:
|
|
38
|
+
- name: single_e-_10GeV
|
|
39
|
+
n_events: 500
|
|
40
|
+
ddsim_args: >-
|
|
41
|
+
--enableGun
|
|
42
|
+
--gun.particle e-
|
|
43
|
+
--gun.distribution uniform
|
|
44
|
+
--gun.energy 10*GeV
|
|
45
|
+
|
|
46
|
+
- name: p8_ee_Zbb_ecm91
|
|
47
|
+
n_events: 100
|
|
48
|
+
input_files: root://eospublic.cern.ch//eos/experiment/fcc/ee/generation/hepmc/p8_ee_Zbb_ecm91/events_noVtxSmear.hepmc
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# k4Bench nightly benchmark config — IDEA o1 v03
|
|
2
|
+
#
|
|
3
|
+
# The nightly workflow expands every file in .github/benchmarks/ into a matrix
|
|
4
|
+
# of (detector × sample) jobs and uploads each result to EOS under:
|
|
5
|
+
# {detector}/{platform}/key4hep-{release}/{sample}/{date}/
|
|
6
|
+
# To add a new detector, drop a new <detector>.yml file in this directory.
|
|
7
|
+
# To add a new sample, append an entry to samples: below.
|
|
8
|
+
#
|
|
9
|
+
# ── Top-level keys (apply to every sample in this file) ────────────────────
|
|
10
|
+
# xml required detector geometry, $K4GEO-relative or absolute path
|
|
11
|
+
# verbose optional stream ddsim output to stdout (default: false)
|
|
12
|
+
# sweep optional baseline + one run per subdetector dropped
|
|
13
|
+
# include_only optional keep only these subdetectors (list, single run)
|
|
14
|
+
# exclude_only optional drop these subdetectors (list, single run)
|
|
15
|
+
# ddsim_args optional ddsim flags applied to every sample
|
|
16
|
+
# steering_file optional ddsim --steeringFile path; $VAR refs (e.g. $FCCCONFIG)
|
|
17
|
+
# are expanded inside the runner. Steering files for FCC
|
|
18
|
+
# detectors live in https://github.com/HEP-FCC/FCC-config
|
|
19
|
+
# and are installed under $FCCCONFIG by Key4hep, e.g.
|
|
20
|
+
# $FCCCONFIG/FullSim/IDEA/IDEA_o1_v03/SteeringFile_IDEA_o1_v03.py
|
|
21
|
+
# samples required list of sample entries — see below
|
|
22
|
+
#
|
|
23
|
+
# ── Per-sample keys (under samples:) ───────────────────────────────────────
|
|
24
|
+
# name required slug — becomes the EOS sample directory + job label
|
|
25
|
+
# n_events required positive integer
|
|
26
|
+
# ddsim_args optional appended to top-level ddsim_args (not replaced)
|
|
27
|
+
# input_files optional HepMC path(s); mutually exclusive with --enableGun
|
|
28
|
+
# steering_file optional overrides the top-level steering_file for this sample
|
|
29
|
+
#
|
|
30
|
+
# Sample-level keys override top-level, except ddsim_args (which concatenates).
|
|
31
|
+
# sweep / include_only / exclude_only are mutually exclusive.
|
|
32
|
+
|
|
33
|
+
xml: FCCee/IDEA/compact/IDEA_o1_v03/IDEA_o1_v03.xml
|
|
34
|
+
steering_file: $FCCCONFIG/FullSim/IDEA/IDEA_o1_v03/SteeringFile_IDEA_o1_v03.py
|
|
35
|
+
|
|
36
|
+
verbose: false
|
|
37
|
+
sweep: false
|
|
38
|
+
|
|
39
|
+
samples:
|
|
40
|
+
- name: single_e-_10GeV
|
|
41
|
+
n_events: 100
|
|
42
|
+
ddsim_args: >-
|
|
43
|
+
--enableGun
|
|
44
|
+
--gun.particle e-
|
|
45
|
+
--gun.distribution uniform
|
|
46
|
+
--gun.energy 10*GeV
|
|
47
|
+
|
|
48
|
+
- name: p8_ee_Zbb_ecm91
|
|
49
|
+
n_events: 100
|
|
50
|
+
input_files: root://eospublic.cern.ch//eos/experiment/fcc/ee/generation/hepmc/p8_ee_Zbb_ecm91/events_noVtxSmear.hepmc
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# k4Bench nightly benchmark config — IDEA o2 v01
|
|
2
|
+
#
|
|
3
|
+
# The nightly workflow expands every file in .github/benchmarks/ into a matrix
|
|
4
|
+
# of (detector × sample) jobs and uploads each result to EOS under:
|
|
5
|
+
# {detector}/{platform}/key4hep-{release}/{sample}/{date}/
|
|
6
|
+
# To add a new detector, drop a new <detector>.yml file in this directory.
|
|
7
|
+
# To add a new sample, append an entry to samples: below.
|
|
8
|
+
#
|
|
9
|
+
# ── Top-level keys (apply to every sample in this file) ────────────────────
|
|
10
|
+
# xml required detector geometry, $K4GEO-relative or absolute path
|
|
11
|
+
# verbose optional stream ddsim output to stdout (default: false)
|
|
12
|
+
# sweep optional baseline + one run per subdetector dropped
|
|
13
|
+
# include_only optional keep only these subdetectors (list, single run)
|
|
14
|
+
# exclude_only optional drop these subdetectors (list, single run)
|
|
15
|
+
# ddsim_args optional ddsim flags applied to every sample
|
|
16
|
+
# steering_file optional ddsim --steeringFile path; $VAR refs (e.g. $FCCCONFIG)
|
|
17
|
+
# are expanded inside the runner. Steering files for FCC
|
|
18
|
+
# detectors live in https://github.com/HEP-FCC/FCC-config
|
|
19
|
+
# and are installed under $FCCCONFIG by Key4hep, e.g.
|
|
20
|
+
# $FCCCONFIG/FullSim/IDEA/IDEA_o2_v01/SteeringFile_IDEA_o2_v01.py
|
|
21
|
+
# samples required list of sample entries — see below
|
|
22
|
+
#
|
|
23
|
+
# ── Per-sample keys (under samples:) ───────────────────────────────────────
|
|
24
|
+
# name required slug — becomes the EOS sample directory + job label
|
|
25
|
+
# n_events required positive integer
|
|
26
|
+
# ddsim_args optional appended to top-level ddsim_args (not replaced)
|
|
27
|
+
# input_files optional HepMC path(s); mutually exclusive with --enableGun
|
|
28
|
+
# steering_file optional overrides the top-level steering_file for this sample
|
|
29
|
+
#
|
|
30
|
+
# Sample-level keys override top-level, except ddsim_args (which concatenates).
|
|
31
|
+
# sweep / include_only / exclude_only are mutually exclusive.
|
|
32
|
+
|
|
33
|
+
xml: FCCee/IDEA/compact/IDEA_o2_v01/IDEA_o2_v01.xml
|
|
34
|
+
steering_file: $FCCCONFIG/FullSim/IDEA/IDEA_o2_v01/SteeringFile_IDEA_o2_v01.py
|
|
35
|
+
|
|
36
|
+
verbose: false
|
|
37
|
+
sweep: false
|
|
38
|
+
|
|
39
|
+
samples:
|
|
40
|
+
- name: single_e-_10GeV
|
|
41
|
+
n_events: 100
|
|
42
|
+
ddsim_args: >-
|
|
43
|
+
--enableGun
|
|
44
|
+
--gun.particle e-
|
|
45
|
+
--gun.distribution uniform
|
|
46
|
+
--gun.energy 10*GeV
|
|
47
|
+
|
|
48
|
+
- name: p8_ee_Zbb_ecm91
|
|
49
|
+
n_events: 100
|
|
50
|
+
input_files: root://eospublic.cern.ch//eos/experiment/fcc/ee/generation/hepmc/p8_ee_Zbb_ecm91/events_noVtxSmear.hepmc
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Expand .github/benchmarks/*.yml into benchmark jobs and print them as JSON
|
|
4
|
+
on stdout. Two output shapes:
|
|
5
|
+
|
|
6
|
+
default — flat list, one record per (detector, sample)
|
|
7
|
+
--grouped — list of {detector, samples: [...]} so the nightly workflow can
|
|
8
|
+
fan out one reusable-workflow call per detector and nest the
|
|
9
|
+
sample matrix beneath it in the Actions UI
|
|
10
|
+
|
|
11
|
+
Each sample record is a fully-merged config consumed downstream as plain env
|
|
12
|
+
vars — no YAML parsing happens after this script runs.
|
|
13
|
+
|
|
14
|
+
Top-level keys in a benchmark file are detector-wide defaults; keys inside a
|
|
15
|
+
samples[] entry override them for that sample only. The single exception is
|
|
16
|
+
ddsim_args: top-level and sample-level strings are concatenated (top first),
|
|
17
|
+
which lets shared ddsim flags live at the detector level. Lists are joined
|
|
18
|
+
to space-separated strings so they round-trip through env vars unchanged.
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import json
|
|
23
|
+
import re
|
|
24
|
+
import shlex
|
|
25
|
+
import sys
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
import yaml
|
|
29
|
+
|
|
30
|
+
BENCH_DIR = Path(".github/benchmarks")
|
|
31
|
+
CONFIG_RE = re.compile(r"^[A-Za-z0-9_-]+$")
|
|
32
|
+
SAMPLE_RE = re.compile(r"^[A-Za-z0-9_.+-]+$")
|
|
33
|
+
|
|
34
|
+
SCALAR_KEYS = ("xml", "n_events", "ddsim_args", "verbose", "sweep", "steering_file")
|
|
35
|
+
LIST_KEYS = ("input_files", "include_only", "exclude_only")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _scalar(v) -> str:
|
|
39
|
+
if v is None: return ""
|
|
40
|
+
if isinstance(v, bool): return str(v).lower()
|
|
41
|
+
return str(v).strip()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _list(v) -> str:
|
|
45
|
+
if v is None: return ""
|
|
46
|
+
if isinstance(v, list): return " ".join(str(x) for x in v)
|
|
47
|
+
return str(v).strip()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _die(msg: str) -> None:
|
|
51
|
+
print(f"ERROR: {msg}", file=sys.stderr)
|
|
52
|
+
sys.exit(1)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def expand(path: Path) -> list[dict]:
|
|
56
|
+
config = path.stem
|
|
57
|
+
if not CONFIG_RE.match(config):
|
|
58
|
+
_die(f"invalid config name {config!r} (from {path})")
|
|
59
|
+
|
|
60
|
+
cfg = yaml.safe_load(path.read_text()) or {}
|
|
61
|
+
if not isinstance(cfg, dict):
|
|
62
|
+
_die(f"top-level YAML must be a mapping in {path}")
|
|
63
|
+
samples = cfg.get("samples") or []
|
|
64
|
+
if not isinstance(samples, list):
|
|
65
|
+
_die(f"'samples' must be a list in {path}")
|
|
66
|
+
if not samples:
|
|
67
|
+
_die(f"no samples defined in {path}")
|
|
68
|
+
|
|
69
|
+
records = []
|
|
70
|
+
for s in samples:
|
|
71
|
+
if not isinstance(s, dict):
|
|
72
|
+
_die(f"sample entries in {path} must be mappings, got {type(s).__name__}")
|
|
73
|
+
name = s.get("name")
|
|
74
|
+
if not name:
|
|
75
|
+
_die(f"sample entry in {path} is missing 'name'")
|
|
76
|
+
if not SAMPLE_RE.match(name):
|
|
77
|
+
_die(f"invalid sample name {name!r} in {path}")
|
|
78
|
+
|
|
79
|
+
def merge(k):
|
|
80
|
+
# ddsim_args concatenates (top + sample); everything else overrides.
|
|
81
|
+
if k == "ddsim_args":
|
|
82
|
+
parts = [v for v in (cfg.get(k), s.get(k)) if v]
|
|
83
|
+
return " ".join(str(p).strip() for p in parts) if parts else None
|
|
84
|
+
return s[k] if k in s else cfg.get(k)
|
|
85
|
+
|
|
86
|
+
rec = {"config": config, "sample": name}
|
|
87
|
+
for k in SCALAR_KEYS: rec[k] = _scalar(merge(k))
|
|
88
|
+
for k in LIST_KEYS: rec[k] = _list(merge(k))
|
|
89
|
+
|
|
90
|
+
loc = f"{path}::{name}"
|
|
91
|
+
if not rec["n_events"].isdigit() or int(rec["n_events"]) <= 0:
|
|
92
|
+
_die(f"n_events must be a positive integer ({loc})")
|
|
93
|
+
if rec["input_files"] and "--enableGun" in shlex.split(rec["ddsim_args"]):
|
|
94
|
+
_die(f"input_files and '--enableGun' in ddsim_args are mutually exclusive ({loc})")
|
|
95
|
+
modes = (rec["sweep"] == "true") + bool(rec["include_only"]) + bool(rec["exclude_only"])
|
|
96
|
+
if modes > 1:
|
|
97
|
+
_die(f"sweep / include_only / exclude_only are mutually exclusive ({loc})")
|
|
98
|
+
|
|
99
|
+
records.append(rec)
|
|
100
|
+
return records
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _group_by_detector(items: list[dict]) -> list[dict]:
|
|
104
|
+
grouped: dict[str, list[dict]] = {}
|
|
105
|
+
for item in items:
|
|
106
|
+
grouped.setdefault(item["config"], []).append(item)
|
|
107
|
+
return [{"detector": d, "samples": s} for d, s in grouped.items()]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def main() -> None:
|
|
111
|
+
grouped = "--grouped" in sys.argv[1:]
|
|
112
|
+
paths = sorted(BENCH_DIR.glob("*.yml"))
|
|
113
|
+
if not paths:
|
|
114
|
+
_die(f"no benchmark configs found in {BENCH_DIR}/")
|
|
115
|
+
items = [r for p in paths for r in expand(p)]
|
|
116
|
+
print(json.dumps(_group_by_detector(items) if grouped else items))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
main()
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Collect machine info around a benchmark run.
|
|
4
|
+
|
|
5
|
+
machine_info.py start <output_dir> # snapshot before the benchmark
|
|
6
|
+
machine_info.py finalize <output_dir> # snapshot after, merged with start
|
|
7
|
+
|
|
8
|
+
The start step writes <output_dir>/_machine_info_start.json with static
|
|
9
|
+
fields (CPU model, RAM total, OS, kernel) and "_start" baselines for the
|
|
10
|
+
dynamic fields (load, free memory, swap, CPU frequency, thermal throttle
|
|
11
|
+
counter). The finalize step reads that file, adds matching "_end" fields,
|
|
12
|
+
computes thermal_throttle_events as the delta over the run, writes
|
|
13
|
+
<output_dir>/machine_info.json, and removes the intermediate start file.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import glob
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import platform
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
START_FILE = "_machine_info_start.json"
|
|
25
|
+
FINAL_FILE = "machine_info.json"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _read(path: str, default: str = "") -> str:
|
|
29
|
+
try:
|
|
30
|
+
with open(path) as f:
|
|
31
|
+
return f.read()
|
|
32
|
+
except OSError:
|
|
33
|
+
return default
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _parse_meminfo() -> dict[str, int]:
|
|
37
|
+
out: dict[str, int] = {}
|
|
38
|
+
for line in _read("/proc/meminfo").splitlines():
|
|
39
|
+
p = line.split()
|
|
40
|
+
if len(p) >= 2:
|
|
41
|
+
try:
|
|
42
|
+
out[p[0].rstrip(":")] = int(p[1])
|
|
43
|
+
except ValueError:
|
|
44
|
+
pass
|
|
45
|
+
return out
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _loadavg() -> list[str]:
|
|
49
|
+
return _read("/proc/loadavg").split()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _cpu_freq_mhz() -> float | None:
|
|
53
|
+
raw = _read("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq").strip()
|
|
54
|
+
if not raw:
|
|
55
|
+
return None
|
|
56
|
+
try:
|
|
57
|
+
return round(int(raw) / 1000, 1)
|
|
58
|
+
except ValueError:
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _throttle_paths() -> list[str]:
|
|
63
|
+
return glob.glob("/sys/devices/system/cpu/cpu*/thermal_throttle/core_throttle_count")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _sum_throttle(paths: list[str]) -> int | None:
|
|
67
|
+
if not paths:
|
|
68
|
+
return None
|
|
69
|
+
total = 0
|
|
70
|
+
for p in paths:
|
|
71
|
+
raw = _read(p).strip()
|
|
72
|
+
try:
|
|
73
|
+
total += int(raw)
|
|
74
|
+
except ValueError:
|
|
75
|
+
pass
|
|
76
|
+
return total
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _kib_to_gib(kib: int) -> float:
|
|
80
|
+
return round(kib / 1024**2, 2)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def collect_start() -> dict:
|
|
84
|
+
cpuinfo = _read("/proc/cpuinfo")
|
|
85
|
+
cpu_model = next(
|
|
86
|
+
(line.split(":", 1)[1].strip() for line in cpuinfo.splitlines() if "model name" in line),
|
|
87
|
+
"unknown",
|
|
88
|
+
)
|
|
89
|
+
# Tolerate any whitespace between the field name and the colon.
|
|
90
|
+
cpu_logical = sum(1 for line in cpuinfo.splitlines() if line.startswith("processor"))
|
|
91
|
+
|
|
92
|
+
# Count unique (physical_id, core_id) pairs — more accurate than socket count
|
|
93
|
+
# alone. Falls back to logical count if these fields are absent (VMs, containers).
|
|
94
|
+
pairs: set = set()
|
|
95
|
+
current: dict = {}
|
|
96
|
+
|
|
97
|
+
def _flush(block: dict) -> None:
|
|
98
|
+
if "processor" in block:
|
|
99
|
+
pairs.add((
|
|
100
|
+
block.get("physical id", "0"),
|
|
101
|
+
block.get("core id", block.get("processor", "0")),
|
|
102
|
+
))
|
|
103
|
+
|
|
104
|
+
for line in cpuinfo.splitlines():
|
|
105
|
+
if not line.strip():
|
|
106
|
+
_flush(current)
|
|
107
|
+
current = {}
|
|
108
|
+
elif ":" in line:
|
|
109
|
+
k, _, v = line.partition(":")
|
|
110
|
+
current[k.strip()] = v.strip()
|
|
111
|
+
_flush(current) # capture the final block when no trailing blank line
|
|
112
|
+
cpu_physical = len(pairs) if pairs else cpu_logical
|
|
113
|
+
cpu_flags = next(
|
|
114
|
+
(line.split(":", 1)[1].strip().split() for line in cpuinfo.splitlines() if line.startswith("flags")),
|
|
115
|
+
[],
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
mem = _parse_meminfo()
|
|
119
|
+
loadavg = _loadavg()
|
|
120
|
+
swap_used_kib = mem.get("SwapTotal", 0) - mem.get("SwapFree", mem.get("SwapTotal", 0))
|
|
121
|
+
|
|
122
|
+
os_name = next(
|
|
123
|
+
(line.split("=", 1)[1].strip('"') for line in _read("/etc/os-release").splitlines()
|
|
124
|
+
if line.startswith("PRETTY_NAME=")),
|
|
125
|
+
"unknown",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
"cpu_model": cpu_model,
|
|
130
|
+
"cpu_physical_cores": cpu_physical,
|
|
131
|
+
"cpu_logical_cores": cpu_logical,
|
|
132
|
+
"cpu_flags": cpu_flags,
|
|
133
|
+
"ram_total_gb": _kib_to_gib(mem.get("MemTotal", 0)),
|
|
134
|
+
"ram_available_gb_start": _kib_to_gib(mem.get("MemAvailable", 0)),
|
|
135
|
+
"swap_total_gb": _kib_to_gib(mem.get("SwapTotal", 0)),
|
|
136
|
+
"swap_used_gb_start": _kib_to_gib(swap_used_kib),
|
|
137
|
+
"load_avg_1m_start": float(loadavg[0]) if len(loadavg) > 0 else None,
|
|
138
|
+
"load_avg_5m_start": float(loadavg[1]) if len(loadavg) > 1 else None,
|
|
139
|
+
"cpu_governor": _read("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor").strip() or None,
|
|
140
|
+
"cpu_freq_mhz_start": _cpu_freq_mhz(),
|
|
141
|
+
"thermal_throttle_count_start": _sum_throttle(_throttle_paths()),
|
|
142
|
+
"kernel": platform.release(),
|
|
143
|
+
"os": os_name,
|
|
144
|
+
"hostname": os.uname().nodename,
|
|
145
|
+
"in_container": os.path.exists("/.dockerenv"),
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def finalize(machine_info: dict) -> dict:
|
|
150
|
+
mem = _parse_meminfo()
|
|
151
|
+
loadavg = _loadavg()
|
|
152
|
+
swap_used_kib = mem.get("SwapTotal", 0) - mem.get("SwapFree", mem.get("SwapTotal", 0))
|
|
153
|
+
|
|
154
|
+
machine_info["ram_available_gb_end"] = _kib_to_gib(mem.get("MemAvailable", 0))
|
|
155
|
+
machine_info["swap_used_gb_end"] = _kib_to_gib(swap_used_kib)
|
|
156
|
+
machine_info["load_avg_1m_end"] = float(loadavg[0]) if len(loadavg) > 0 else None
|
|
157
|
+
machine_info["load_avg_5m_end"] = float(loadavg[1]) if len(loadavg) > 1 else None
|
|
158
|
+
machine_info["cpu_freq_mhz_end"] = _cpu_freq_mhz()
|
|
159
|
+
|
|
160
|
+
paths = _throttle_paths()
|
|
161
|
+
end = _sum_throttle(paths)
|
|
162
|
+
start = machine_info.get("thermal_throttle_count_start")
|
|
163
|
+
machine_info["thermal_throttle_events"] = (
|
|
164
|
+
max(0, end - start) if (paths and start is not None and end is not None) else None
|
|
165
|
+
)
|
|
166
|
+
return machine_info
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def cmd_start(out_dir: Path) -> None:
|
|
170
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
171
|
+
info = collect_start()
|
|
172
|
+
(out_dir / START_FILE).write_text(json.dumps(info, indent=2))
|
|
173
|
+
print(f"cpu_model : {info['cpu_model']}")
|
|
174
|
+
print(f"cpu_logical_cores: {info['cpu_logical_cores']}")
|
|
175
|
+
print(f"ram_total_gb : {info['ram_total_gb']:.2f} GB")
|
|
176
|
+
print(f"ram_available : {info['ram_available_gb_start']:.2f} GB")
|
|
177
|
+
print(f"load_avg_1m : {info['load_avg_1m_start']}")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def cmd_finalize(out_dir: Path) -> None:
|
|
181
|
+
start_path = out_dir / START_FILE
|
|
182
|
+
if start_path.exists():
|
|
183
|
+
info = json.loads(start_path.read_text())
|
|
184
|
+
start_path.unlink()
|
|
185
|
+
else:
|
|
186
|
+
print(f"WARNING: {start_path} not found — machine info start snapshot missing", flush=True)
|
|
187
|
+
info = {}
|
|
188
|
+
info = finalize(info)
|
|
189
|
+
(out_dir / FINAL_FILE).write_text(json.dumps(info, indent=2))
|
|
190
|
+
print(f"Written: {out_dir / FINAL_FILE}")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def main() -> None:
|
|
194
|
+
if len(sys.argv) != 3 or sys.argv[1] not in ("start", "finalize"):
|
|
195
|
+
sys.exit(f"usage: {sys.argv[0]} (start|finalize) <output_dir>")
|
|
196
|
+
cmd = sys.argv[1]
|
|
197
|
+
out_dir = Path(sys.argv[2])
|
|
198
|
+
(cmd_start if cmd == "start" else cmd_finalize)(out_dir)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if __name__ == "__main__":
|
|
202
|
+
main()
|