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.
Files changed (65) hide show
  1. benchmark/__init__.py +13 -0
  2. benchmark/__main__.py +53 -0
  3. benchmark/compilers.py +64 -0
  4. benchmark/config.py +131 -0
  5. benchmark/harness.py +149 -0
  6. benchmark/profilers.py +281 -0
  7. benchmark/results.py +278 -0
  8. benchmark/suites/__init__.py +18 -0
  9. benchmark/suites/base.py +60 -0
  10. benchmark/suites/benchmarksgame.py +145 -0
  11. benchmark/suites/dacapo.py +45 -0
  12. benchmark/suites/perfopt.py +144 -0
  13. benchmark/suites/renaissance.py +48 -0
  14. benchmark/validation/__init__.py +9 -0
  15. benchmark/validation/analysis.py +73 -0
  16. benchmark/validation/experiments.py +247 -0
  17. benchmark/validation/reporting.py +145 -0
  18. codegreen/__init__.py +34 -0
  19. codegreen/analysis/__init__.py +0 -0
  20. codegreen/analysis/_ts_java.py +23 -0
  21. codegreen/analysis/cfg/__init__.py +31 -0
  22. codegreen/analysis/cfg/builder.py +367 -0
  23. codegreen/analysis/cfg/callgraph.py +185 -0
  24. codegreen/analysis/cfg/dataflow.py +53 -0
  25. codegreen/analysis/cfg/energy_flow.py +725 -0
  26. codegreen/analysis/cfg/features.py +168 -0
  27. codegreen/analysis/cfg/pdg.py +239 -0
  28. codegreen/analysis/cfg/types.py +59 -0
  29. codegreen/analysis/cfg/visualization.py +47 -0
  30. codegreen/analyzer/__init__.py +1 -0
  31. codegreen/analyzer/plot.py +251 -0
  32. codegreen/cli/__init__.py +1 -0
  33. codegreen/cli/cli.py +3261 -0
  34. codegreen/cli/entrypoint.py +30 -0
  35. codegreen/config.json +177 -0
  36. codegreen/instrumentation/__init__.py +10 -0
  37. codegreen/instrumentation/ast_processor.py +1868 -0
  38. codegreen/instrumentation/bridge_analyze.py +59 -0
  39. codegreen/instrumentation/bridge_instrument.py +70 -0
  40. codegreen/instrumentation/config.py +73 -0
  41. codegreen/instrumentation/configs/TEMPLATE.json +80 -0
  42. codegreen/instrumentation/configs/c.json +229 -0
  43. codegreen/instrumentation/configs/cpp.json +258 -0
  44. codegreen/instrumentation/configs/java.json +280 -0
  45. codegreen/instrumentation/configs/javascript.json +242 -0
  46. codegreen/instrumentation/configs/python.json +266 -0
  47. codegreen/instrumentation/engine.py +898 -0
  48. codegreen/instrumentation/language_configs.py +217 -0
  49. codegreen/instrumentation/language_engine.py +2168 -0
  50. codegreen/instrumentation/language_runtimes/c/codegreen_runtime.h +45 -0
  51. codegreen/instrumentation/language_runtimes/cpp/codegreen/runtime.hpp +32 -0
  52. codegreen/instrumentation/language_runtimes/java/codegreen/runtime/CodeGreenRuntime.java +54 -0
  53. codegreen/instrumentation/language_runtimes/java/codegreen/runtime/CodeGreenStandaloneRuntime.java +44 -0
  54. codegreen/instrumentation/language_runtimes/python/codegreen_runtime.py +194 -0
  55. codegreen/lib/codegreen-nemb.dll +0 -0
  56. codegreen/lib/runtime/c/codegreen_runtime.h +45 -0
  57. codegreen/utils/__init__.py +8 -0
  58. codegreen/utils/binary.py +59 -0
  59. codegreen/utils/platform.py +42 -0
  60. codegreen-0.3.2.dist-info/METADATA +602 -0
  61. codegreen-0.3.2.dist-info/RECORD +65 -0
  62. codegreen-0.3.2.dist-info/WHEEL +5 -0
  63. codegreen-0.3.2.dist-info/entry_points.txt +2 -0
  64. codegreen-0.3.2.dist-info/licenses/LICENSE +373 -0
  65. 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()