codegreen 0.3.2__cp313-cp313-win_amd64.whl
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.
- benchmark/__init__.py +13 -0
- benchmark/__main__.py +53 -0
- benchmark/compilers.py +64 -0
- benchmark/config.py +131 -0
- benchmark/harness.py +149 -0
- benchmark/profilers.py +281 -0
- benchmark/results.py +278 -0
- benchmark/suites/__init__.py +18 -0
- benchmark/suites/base.py +60 -0
- benchmark/suites/benchmarksgame.py +145 -0
- benchmark/suites/dacapo.py +45 -0
- benchmark/suites/perfopt.py +144 -0
- benchmark/suites/renaissance.py +48 -0
- benchmark/validation/__init__.py +9 -0
- benchmark/validation/analysis.py +73 -0
- benchmark/validation/experiments.py +247 -0
- benchmark/validation/reporting.py +145 -0
- codegreen/__init__.py +34 -0
- codegreen/analysis/__init__.py +0 -0
- codegreen/analysis/_ts_java.py +23 -0
- codegreen/analysis/cfg/__init__.py +31 -0
- codegreen/analysis/cfg/builder.py +367 -0
- codegreen/analysis/cfg/callgraph.py +185 -0
- codegreen/analysis/cfg/dataflow.py +53 -0
- codegreen/analysis/cfg/energy_flow.py +725 -0
- codegreen/analysis/cfg/features.py +168 -0
- codegreen/analysis/cfg/pdg.py +239 -0
- codegreen/analysis/cfg/types.py +59 -0
- codegreen/analysis/cfg/visualization.py +47 -0
- codegreen/analyzer/__init__.py +1 -0
- codegreen/analyzer/plot.py +251 -0
- codegreen/cli/__init__.py +1 -0
- codegreen/cli/cli.py +3261 -0
- codegreen/cli/entrypoint.py +30 -0
- codegreen/config.json +177 -0
- codegreen/instrumentation/__init__.py +10 -0
- codegreen/instrumentation/ast_processor.py +1868 -0
- codegreen/instrumentation/bridge_analyze.py +59 -0
- codegreen/instrumentation/bridge_instrument.py +70 -0
- codegreen/instrumentation/config.py +73 -0
- codegreen/instrumentation/configs/TEMPLATE.json +80 -0
- codegreen/instrumentation/configs/c.json +229 -0
- codegreen/instrumentation/configs/cpp.json +258 -0
- codegreen/instrumentation/configs/java.json +280 -0
- codegreen/instrumentation/configs/javascript.json +242 -0
- codegreen/instrumentation/configs/python.json +266 -0
- codegreen/instrumentation/engine.py +898 -0
- codegreen/instrumentation/language_configs.py +217 -0
- codegreen/instrumentation/language_engine.py +2168 -0
- codegreen/instrumentation/language_runtimes/c/codegreen_runtime.h +45 -0
- codegreen/instrumentation/language_runtimes/cpp/codegreen/runtime.hpp +32 -0
- codegreen/instrumentation/language_runtimes/java/codegreen/runtime/CodeGreenRuntime.java +54 -0
- codegreen/instrumentation/language_runtimes/java/codegreen/runtime/CodeGreenStandaloneRuntime.java +44 -0
- codegreen/instrumentation/language_runtimes/python/codegreen_runtime.py +194 -0
- codegreen/lib/codegreen-nemb.dll +0 -0
- codegreen/lib/runtime/c/codegreen_runtime.h +45 -0
- codegreen/utils/__init__.py +8 -0
- codegreen/utils/binary.py +59 -0
- codegreen/utils/platform.py +42 -0
- codegreen-0.3.2.dist-info/METADATA +602 -0
- codegreen-0.3.2.dist-info/RECORD +65 -0
- codegreen-0.3.2.dist-info/WHEEL +5 -0
- codegreen-0.3.2.dist-info/entry_points.txt +2 -0
- codegreen-0.3.2.dist-info/licenses/LICENSE +373 -0
- codegreen-0.3.2.dist-info/top_level.txt +2 -0
benchmark/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""CodeGreen Benchmarking Module - Energy measurement benchmarking harness."""
|
|
2
|
+
from benchmark.config import BenchmarkConfig, Problem, RunResult
|
|
3
|
+
from benchmark.harness import BenchmarkHarness
|
|
4
|
+
from benchmark.profilers import CodeGreenProfiler, PerfProfiler
|
|
5
|
+
from benchmark.results import ResultCollector, StatisticalAnalysis, ComparisonReport
|
|
6
|
+
from benchmark.suites import get_suite, SUITES
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"BenchmarkConfig", "Problem", "RunResult",
|
|
10
|
+
"BenchmarkHarness", "CodeGreenProfiler", "PerfProfiler",
|
|
11
|
+
"ResultCollector", "StatisticalAnalysis", "ComparisonReport",
|
|
12
|
+
"get_suite", "SUITES",
|
|
13
|
+
]
|
benchmark/__main__.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Entry point for `python3 -m benchmark`."""
|
|
2
|
+
import argparse
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from benchmark.config import BenchmarkConfig
|
|
6
|
+
from benchmark.harness import BenchmarkHarness
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
parser = argparse.ArgumentParser(description="CodeGreen Energy Benchmark Suite")
|
|
10
|
+
parser.add_argument("--problems", nargs="+", help="Problems to run (default: all)")
|
|
11
|
+
parser.add_argument("--languages", nargs="+", default=["python"], help="Languages (default: python)")
|
|
12
|
+
parser.add_argument("--sizes", nargs="+", help="Input sizes (default: from config)")
|
|
13
|
+
parser.add_argument("--profilers", nargs="+", default=["codegreen"], help="Profilers (default: codegreen)")
|
|
14
|
+
parser.add_argument("--repetitions", type=int, default=5, help="Repetitions per run (default: 5)")
|
|
15
|
+
parser.add_argument("--output", type=Path, help="Output JSON file path")
|
|
16
|
+
parser.add_argument("--csv", type=Path, help="Output CSV file path")
|
|
17
|
+
parser.add_argument("--config", type=Path, help="YAML config file")
|
|
18
|
+
args = parser.parse_args()
|
|
19
|
+
|
|
20
|
+
config = BenchmarkConfig.from_yaml(args.config) if args.config else BenchmarkConfig.default()
|
|
21
|
+
harness = BenchmarkHarness(config)
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
collector = harness.run_suite(
|
|
25
|
+
problems=args.problems,
|
|
26
|
+
languages=args.languages,
|
|
27
|
+
sizes=args.sizes,
|
|
28
|
+
profilers=args.profilers,
|
|
29
|
+
repetitions=args.repetitions,
|
|
30
|
+
)
|
|
31
|
+
summaries = collector.summarize_all()
|
|
32
|
+
print(f"\n{'='*60}")
|
|
33
|
+
print(f"Results: {len(collector.results)} runs")
|
|
34
|
+
print(f"{'='*60}")
|
|
35
|
+
for key, data in sorted(summaries.items()):
|
|
36
|
+
e = data.get("energy")
|
|
37
|
+
t = data.get("time")
|
|
38
|
+
valid = data.get("valid_runs", 0)
|
|
39
|
+
total = data.get("total_runs", 0)
|
|
40
|
+
energy_str = f"{e.mean:.4f} +/- {e.std:.4f} J" if e else "N/A"
|
|
41
|
+
print(f" {key}: energy={energy_str} time={t.mean:.3f}s valid={valid}/{total}")
|
|
42
|
+
|
|
43
|
+
if args.output:
|
|
44
|
+
collector.to_json(args.output)
|
|
45
|
+
print(f"\nJSON saved: {args.output}")
|
|
46
|
+
if args.csv:
|
|
47
|
+
collector.to_csv(args.csv)
|
|
48
|
+
print(f"CSV saved: {args.csv}")
|
|
49
|
+
finally:
|
|
50
|
+
harness.cleanup()
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
main()
|
benchmark/compilers.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Compiler management for C/C++/Java benchmarks."""
|
|
2
|
+
import subprocess
|
|
3
|
+
import tempfile
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
from benchmark.config import LanguageEnv
|
|
7
|
+
|
|
8
|
+
class CompilerManager:
|
|
9
|
+
def __init__(self, build_dir: Optional[Path] = None):
|
|
10
|
+
self.build_dir = build_dir or Path(tempfile.mkdtemp(prefix="codegreen_bench_"))
|
|
11
|
+
self.build_dir.mkdir(parents=True, exist_ok=True)
|
|
12
|
+
|
|
13
|
+
def compile(self, source: Path, env: LanguageEnv) -> Path:
|
|
14
|
+
if not env.compiler:
|
|
15
|
+
return source
|
|
16
|
+
lang = self._detect_language(source, env)
|
|
17
|
+
if lang == "java":
|
|
18
|
+
return self._compile_java(source, env)
|
|
19
|
+
return self._compile_native(source, env, lang)
|
|
20
|
+
|
|
21
|
+
def _detect_language(self, source: Path, env: LanguageEnv) -> str:
|
|
22
|
+
if env.compiler == "javac":
|
|
23
|
+
return "java"
|
|
24
|
+
if env.compiler in ("g++", "clang++"):
|
|
25
|
+
return "cpp"
|
|
26
|
+
return "c"
|
|
27
|
+
|
|
28
|
+
def _compile_native(self, source: Path, env: LanguageEnv, lang: str) -> Path:
|
|
29
|
+
binary = self.build_dir / source.stem
|
|
30
|
+
extra_flags = []
|
|
31
|
+
if source.suffix in (".gpp", ".gcc") or ".gpp" in source.name or ".gcc" in source.name:
|
|
32
|
+
extra_flags = ["-x", "c++" if lang == "cpp" else "c"]
|
|
33
|
+
# Separate linker flags (like -lm) from compiler flags - linker flags go at end
|
|
34
|
+
compiler_flags = [f for f in env.flags if not f.startswith('-l')]
|
|
35
|
+
linker_flags = [f for f in env.flags if f.startswith('-l')]
|
|
36
|
+
cmd = [env.compiler] + extra_flags + compiler_flags + [str(source), "-o", str(binary)] + linker_flags
|
|
37
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
|
38
|
+
if result.returncode != 0:
|
|
39
|
+
raise RuntimeError(f"Compilation failed: {result.stderr}")
|
|
40
|
+
return binary
|
|
41
|
+
|
|
42
|
+
def _compile_java(self, source: Path, env: LanguageEnv) -> Path:
|
|
43
|
+
cmd = [env.compiler] + env.flags + ["-d", str(self.build_dir), str(source)]
|
|
44
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
|
45
|
+
if result.returncode != 0:
|
|
46
|
+
raise RuntimeError(f"Java compilation failed: {result.stderr}")
|
|
47
|
+
class_name = source.stem
|
|
48
|
+
return self.build_dir / f"{class_name}.class"
|
|
49
|
+
|
|
50
|
+
def get_run_command(self, source: Path, binary: Path, env: LanguageEnv, args: List[str]) -> List[str]:
|
|
51
|
+
if not env.compiler:
|
|
52
|
+
cmd_str = env.run_cmd.format(source=str(source))
|
|
53
|
+
return cmd_str.split() + args
|
|
54
|
+
if env.compiler == "javac":
|
|
55
|
+
class_name = source.stem
|
|
56
|
+
cmd_str = env.run_cmd.format(build_dir=str(self.build_dir), class_name=class_name)
|
|
57
|
+
return cmd_str.split() + args
|
|
58
|
+
cmd_str = env.run_cmd.format(binary=str(binary))
|
|
59
|
+
return cmd_str.split() + args
|
|
60
|
+
|
|
61
|
+
def cleanup(self):
|
|
62
|
+
import shutil
|
|
63
|
+
if self.build_dir.exists():
|
|
64
|
+
shutil.rmtree(self.build_dir, ignore_errors=True)
|
benchmark/config.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Benchmark configuration dataclasses and YAML loader."""
|
|
2
|
+
import platform
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, List, Optional, Any
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
import yaml
|
|
10
|
+
except ImportError:
|
|
11
|
+
yaml = None
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class SystemState:
|
|
15
|
+
cpu_model: str = ""
|
|
16
|
+
cpu_governor: str = ""
|
|
17
|
+
kernel_version: str = ""
|
|
18
|
+
rapl_domains: List[str] = field(default_factory=list)
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def capture(cls) -> "SystemState":
|
|
22
|
+
state = cls()
|
|
23
|
+
state.kernel_version = platform.release()
|
|
24
|
+
try:
|
|
25
|
+
with open("/proc/cpuinfo") as f:
|
|
26
|
+
for line in f:
|
|
27
|
+
if line.startswith("model name"):
|
|
28
|
+
state.cpu_model = line.split(":", 1)[1].strip()
|
|
29
|
+
break
|
|
30
|
+
except (FileNotFoundError, OSError):
|
|
31
|
+
pass
|
|
32
|
+
try:
|
|
33
|
+
with open("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor") as f:
|
|
34
|
+
state.cpu_governor = f.read().strip()
|
|
35
|
+
except (FileNotFoundError, OSError):
|
|
36
|
+
pass
|
|
37
|
+
import os
|
|
38
|
+
for i in range(10):
|
|
39
|
+
path = f"/sys/class/powercap/intel-rapl:0:{i}/name"
|
|
40
|
+
if os.path.exists(path):
|
|
41
|
+
try:
|
|
42
|
+
with open(path) as f:
|
|
43
|
+
state.rapl_domains.append(f.read().strip())
|
|
44
|
+
except OSError:
|
|
45
|
+
pass
|
|
46
|
+
else:
|
|
47
|
+
break
|
|
48
|
+
return state
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class Problem:
|
|
52
|
+
name: str
|
|
53
|
+
sizes: List[str]
|
|
54
|
+
validation_output: Optional[str] = None
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class LanguageEnv:
|
|
58
|
+
extension: str
|
|
59
|
+
run_cmd: str
|
|
60
|
+
compiler: Optional[str] = None
|
|
61
|
+
flags: List[str] = field(default_factory=list)
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class RunResult:
|
|
65
|
+
problem: str
|
|
66
|
+
language: str
|
|
67
|
+
size: str
|
|
68
|
+
profiler: str
|
|
69
|
+
energy_joules: float
|
|
70
|
+
time_seconds: float
|
|
71
|
+
output_valid: bool
|
|
72
|
+
repetition: int = 0
|
|
73
|
+
variant: str = "default"
|
|
74
|
+
suite: str = "benchmarksgame"
|
|
75
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
76
|
+
checkpoints: List[Dict[str, Any]] = field(default_factory=list)
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class BenchmarkConfig:
|
|
80
|
+
problems: List[Problem]
|
|
81
|
+
languages: Dict[str, LanguageEnv]
|
|
82
|
+
repetitions: int = 30
|
|
83
|
+
warmup_runs: int = 1
|
|
84
|
+
timeout_seconds: int = 60
|
|
85
|
+
clear_cache: bool = True
|
|
86
|
+
cpu_governor: str = "performance"
|
|
87
|
+
min_runtime_seconds: float = 1.0
|
|
88
|
+
mode: str = "local"
|
|
89
|
+
benchmarks_dir: Optional[str] = None
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def from_yaml(cls, path: Path) -> "BenchmarkConfig":
|
|
93
|
+
if yaml is None:
|
|
94
|
+
raise ImportError("PyYAML required for YAML config: pip install pyyaml")
|
|
95
|
+
with open(path) as f:
|
|
96
|
+
data = yaml.safe_load(f)
|
|
97
|
+
cfg = data.get("benchmark", data)
|
|
98
|
+
problems = [Problem(**p) for p in cfg.get("problems", [])]
|
|
99
|
+
languages = {k: LanguageEnv(**v) for k, v in cfg.get("languages", {}).items()}
|
|
100
|
+
return cls(
|
|
101
|
+
problems=problems,
|
|
102
|
+
languages=languages,
|
|
103
|
+
repetitions=cfg.get("repetitions", 30),
|
|
104
|
+
warmup_runs=cfg.get("warmup_runs", 3),
|
|
105
|
+
timeout_seconds=cfg.get("timeout_seconds", 300),
|
|
106
|
+
clear_cache=cfg.get("best_practices", {}).get("clear_cache", True),
|
|
107
|
+
cpu_governor=cfg.get("best_practices", {}).get("cpu_governor", "performance"),
|
|
108
|
+
min_runtime_seconds=cfg.get("min_runtime_seconds", 1.0),
|
|
109
|
+
mode=cfg.get("mode", "local"),
|
|
110
|
+
benchmarks_dir=cfg.get("benchmarks_dir"),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
@classmethod
|
|
114
|
+
def default(cls) -> "BenchmarkConfig":
|
|
115
|
+
return cls(
|
|
116
|
+
problems=[
|
|
117
|
+
Problem("nbody", ["1000", "5000", "50000"], "1000_out"),
|
|
118
|
+
Problem("spectralnorm", ["100", "500", "1000"], "100_out"),
|
|
119
|
+
Problem("binarytrees", ["10", "14", "18"], "10_out"),
|
|
120
|
+
Problem("fannkuchredux", ["7", "10", "11"], "7_out"),
|
|
121
|
+
],
|
|
122
|
+
languages={
|
|
123
|
+
"python": LanguageEnv(".python3", "python3 {source}"),
|
|
124
|
+
"c": LanguageEnv(".gcc", "{binary}", compiler="gcc", flags=["-O3", "-march=native", "-lm", "-lpthread"]),
|
|
125
|
+
"cpp": LanguageEnv(".gpp", "{binary}", compiler="g++", flags=["-O3", "-march=native", "-lpthread"]),
|
|
126
|
+
"java": LanguageEnv(".java", "java -cp {build_dir} {class_name}", compiler="javac", flags=[]),
|
|
127
|
+
},
|
|
128
|
+
repetitions=5,
|
|
129
|
+
warmup_runs=1,
|
|
130
|
+
timeout_seconds=180,
|
|
131
|
+
)
|
benchmark/harness.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Benchmark harness - core execution engine using Suite protocol."""
|
|
2
|
+
import math
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
import time
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Callable, List, Optional
|
|
8
|
+
|
|
9
|
+
from benchmark.config import RunResult
|
|
10
|
+
from benchmark.profilers import ProfilerInterface, PerfProfiler, get_profiler
|
|
11
|
+
from benchmark.results import ResultCollector
|
|
12
|
+
from benchmark.suites.base import Suite, Task
|
|
13
|
+
|
|
14
|
+
SLEEP_BETWEEN_RUNS = 2.0
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _check_cpu_governor(expected: str, callback=None):
|
|
18
|
+
try:
|
|
19
|
+
with open("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor") as f:
|
|
20
|
+
actual = f.read().strip()
|
|
21
|
+
if actual != expected:
|
|
22
|
+
msg = (f"WARNING: CPU governor is '{actual}', expected '{expected}'. "
|
|
23
|
+
f"Results may have higher variance. "
|
|
24
|
+
f"Set with: sudo cpupower frequency-set -g {expected}")
|
|
25
|
+
if callback:
|
|
26
|
+
callback(msg)
|
|
27
|
+
else:
|
|
28
|
+
print(msg, file=sys.stderr)
|
|
29
|
+
except FileNotFoundError:
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class BenchmarkHarness:
|
|
34
|
+
def __init__(self, suite: Optional[Suite] = None,
|
|
35
|
+
repetitions: int = 5, warmup_runs: int = 1,
|
|
36
|
+
timeout_seconds: int = 300, min_runtime_seconds: float = 1.0,
|
|
37
|
+
progress_callback: Optional[Callable] = None):
|
|
38
|
+
self.suite = suite
|
|
39
|
+
self.repetitions = repetitions
|
|
40
|
+
self.warmup_runs = warmup_runs
|
|
41
|
+
self.timeout = timeout_seconds or (suite.default_timeout if suite else 300)
|
|
42
|
+
self.min_runtime = min_runtime_seconds
|
|
43
|
+
self.collector = ResultCollector()
|
|
44
|
+
self.progress_callback = progress_callback or (lambda msg: print(msg, file=sys.stderr))
|
|
45
|
+
_check_cpu_governor("performance", self.progress_callback)
|
|
46
|
+
|
|
47
|
+
def run_task(self, task: Task, profiler_name: str = "perf",
|
|
48
|
+
repetitions: Optional[int] = None) -> List[RunResult]:
|
|
49
|
+
"""Run a single task with a single profiler."""
|
|
50
|
+
reps = repetitions or self.repetitions
|
|
51
|
+
profiler = get_profiler(profiler_name)
|
|
52
|
+
if hasattr(profiler, 'set_source') and task.source_file:
|
|
53
|
+
profiler.set_source(task.source_file, task.language)
|
|
54
|
+
|
|
55
|
+
# Build the task (compile, construct run command)
|
|
56
|
+
task = self.suite.build(task)
|
|
57
|
+
if not task.run_command:
|
|
58
|
+
self.progress_callback(f" No run command for {task.name}, skipping")
|
|
59
|
+
return []
|
|
60
|
+
|
|
61
|
+
results = []
|
|
62
|
+
|
|
63
|
+
# Warmup
|
|
64
|
+
for i in range(self.warmup_runs):
|
|
65
|
+
self.progress_callback(f" Warmup {i+1}/{self.warmup_runs}")
|
|
66
|
+
try:
|
|
67
|
+
profiler.run(task.run_command, timeout=self.timeout)
|
|
68
|
+
except Exception:
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
# Estimate runtime for auto-repeat
|
|
72
|
+
estimated = self._estimate_runtime(task.run_command)
|
|
73
|
+
self._configure_auto_repeat(profiler, estimated)
|
|
74
|
+
self._clear_cache()
|
|
75
|
+
|
|
76
|
+
# Measurement runs
|
|
77
|
+
for i in range(reps):
|
|
78
|
+
if i > 0:
|
|
79
|
+
time.sleep(SLEEP_BETWEEN_RUNS)
|
|
80
|
+
self.progress_callback(f" Run {i+1}/{reps}")
|
|
81
|
+
try:
|
|
82
|
+
profile_result = profiler.run(task.run_command, timeout=self.timeout)
|
|
83
|
+
repeat_count = getattr(profiler, 'repeat_count', 1)
|
|
84
|
+
valid = (True if repeat_count > 1
|
|
85
|
+
else self.suite.validate_output(profile_result.output, task))
|
|
86
|
+
|
|
87
|
+
result = RunResult(
|
|
88
|
+
problem=task.name,
|
|
89
|
+
language=task.language,
|
|
90
|
+
size=task.metadata.get("size", "default"),
|
|
91
|
+
profiler=profiler_name,
|
|
92
|
+
energy_joules=profile_result.energy_joules,
|
|
93
|
+
time_seconds=profile_result.time_seconds,
|
|
94
|
+
output_valid=valid,
|
|
95
|
+
repetition=i + 1,
|
|
96
|
+
variant=task.variant,
|
|
97
|
+
suite=self.suite.name,
|
|
98
|
+
checkpoints=profile_result.checkpoints,
|
|
99
|
+
)
|
|
100
|
+
results.append(result)
|
|
101
|
+
self.collector.add(result)
|
|
102
|
+
except subprocess.TimeoutExpired:
|
|
103
|
+
self.progress_callback(f" Run {i+1} timed out")
|
|
104
|
+
except Exception as e:
|
|
105
|
+
self.progress_callback(f" Run {i+1} failed: {e}")
|
|
106
|
+
return results
|
|
107
|
+
|
|
108
|
+
def run_suite(self, profilers: Optional[List[str]] = None,
|
|
109
|
+
repetitions: Optional[int] = None,
|
|
110
|
+
filters: Optional[dict] = None) -> ResultCollector:
|
|
111
|
+
"""Discover all tasks and run them with specified profilers."""
|
|
112
|
+
profs = profilers or ["perf"]
|
|
113
|
+
tasks = self.suite.discover(filters)
|
|
114
|
+
self.progress_callback(f"Discovered {len(tasks)} tasks in {self.suite.name}")
|
|
115
|
+
|
|
116
|
+
for task in tasks:
|
|
117
|
+
for prof in profs:
|
|
118
|
+
self.progress_callback(f"\n{task.name} [{task.variant}] profiler={prof}")
|
|
119
|
+
self.run_task(task, prof, repetitions)
|
|
120
|
+
return self.collector
|
|
121
|
+
|
|
122
|
+
def _estimate_runtime(self, cmd: List[str]) -> float:
|
|
123
|
+
try:
|
|
124
|
+
start = time.perf_counter()
|
|
125
|
+
subprocess.run(cmd, capture_output=True, text=True, timeout=self.timeout)
|
|
126
|
+
return time.perf_counter() - start
|
|
127
|
+
except Exception:
|
|
128
|
+
return 0.0
|
|
129
|
+
|
|
130
|
+
def _configure_auto_repeat(self, profiler: ProfilerInterface, estimated_time: float):
|
|
131
|
+
if not isinstance(profiler, PerfProfiler):
|
|
132
|
+
return
|
|
133
|
+
if estimated_time > 0 and estimated_time < self.min_runtime:
|
|
134
|
+
repeat = math.ceil(self.min_runtime / estimated_time)
|
|
135
|
+
profiler.repeat_count = repeat
|
|
136
|
+
self.progress_callback(f" Short workload ({estimated_time:.2f}s < {self.min_runtime}s), "
|
|
137
|
+
f"auto-repeat x{repeat}")
|
|
138
|
+
|
|
139
|
+
def _clear_cache(self):
|
|
140
|
+
try:
|
|
141
|
+
subprocess.run(["sync"], check=False)
|
|
142
|
+
subprocess.run(["sudo", "sh", "-c", "echo 3 > /proc/sys/vm/drop_caches"],
|
|
143
|
+
capture_output=True, check=False)
|
|
144
|
+
except Exception:
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
def cleanup(self):
|
|
148
|
+
if self.suite:
|
|
149
|
+
self.suite.cleanup()
|