codegreen 0.4.9__tar.gz → 0.5.0__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.
- {codegreen-0.4.9 → codegreen-0.5.0}/CMakeLists.txt +4 -15
- {codegreen-0.4.9 → codegreen-0.5.0}/MANIFEST.in +0 -1
- {codegreen-0.4.9/codegreen.egg-info → codegreen-0.5.0}/PKG-INFO +2 -2
- {codegreen-0.4.9 → codegreen-0.5.0}/README.md +1 -1
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/__main__.py +10 -14
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/config.py +29 -13
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/harness.py +22 -4
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/profilers.py +74 -116
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/results.py +50 -33
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/suites/benchmarksgame.py +19 -3
- codegreen-0.5.0/benchmark/validation/experiments.py +425 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/__init__.py +5 -10
- codegreen-0.5.0/codegreen/analysis/gpu_metrics.py +236 -0
- codegreen-0.5.0/codegreen/analysis/hwcounters.py +163 -0
- codegreen-0.5.0/codegreen/analysis/languages.py +65 -0
- codegreen-0.5.0/codegreen/analysis/line_sampling.py +288 -0
- codegreen-0.5.0/codegreen/analysis/metrics.py +143 -0
- codegreen-0.5.0/codegreen/analysis/pyalloc.py +65 -0
- codegreen-0.5.0/codegreen/analysis/sampling.py +322 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/cli/cli.py +548 -717
- codegreen-0.5.0/codegreen/config.json +336 -0
- codegreen-0.5.0/codegreen/hw_events/amd_zen.json +2 -0
- codegreen-0.5.0/codegreen/hw_events/arm_neoverse.json +3 -0
- codegreen-0.5.0/codegreen/hw_events/generic.json +5 -0
- codegreen-0.5.0/codegreen/hw_events/intel.json +2 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/__init__.py +1 -2
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/configs/TEMPLATE.json +5 -2
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/configs/c.json +4 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/configs/cpp.json +4 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/configs/java.json +17 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/configs/javascript.json +3 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/configs/python.json +4 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/language_engine.py +73 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/language_runtimes/c/codegreen_runtime.h +8 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/language_runtimes/java/codegreen/runtime/CodeGreenStandaloneRuntime.java +132 -38
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/language_runtimes/python/codegreen_runtime.py +549 -169
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/CMakeLists.txt +3 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/codegreen_energy.hpp +35 -7
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/config_loader.hpp +20 -19
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/core/energy_provider.hpp +94 -7
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/core/measurement_coordinator.hpp +61 -14
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/drivers/amd_gpu_provider.hpp +1 -44
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/drivers/intel_rapl_provider.hpp +3 -1
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/drivers/nvidia_gpu_provider.hpp +31 -39
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/codegreen_energy.cpp +327 -129
- codegreen-0.5.0/codegreen/measurement/src/nemb/config_loader.cpp +207 -0
- codegreen-0.5.0/codegreen/measurement/src/nemb/core/energy_provider.cpp +161 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/core/measurement_coordinator.cpp +135 -87
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/drivers/amd_gpu_provider.cpp +31 -110
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/drivers/amd_rapl_provider.cpp +14 -2
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/drivers/intel_rapl_provider.cpp +39 -61
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/drivers/nvidia_gpu_provider.cpp +101 -69
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/drivers/windows_emi_provider.cpp +2 -0
- codegreen-0.5.0/codegreen/measurement/tests/nemb_validation_test.cpp +547 -0
- {codegreen-0.4.9 → codegreen-0.5.0/codegreen.egg-info}/PKG-INFO +2 -2
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen.egg-info/SOURCES.txt +13 -2
- {codegreen-0.4.9 → codegreen-0.5.0}/pyproject.toml +2 -1
- {codegreen-0.4.9 → codegreen-0.5.0}/setup.py +1 -0
- codegreen-0.5.0/tests/test_gpu_metrics.py +128 -0
- codegreen-0.5.0/tests/test_hwcounters.py +210 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/tests/test_instrumentation.py +259 -12
- codegreen-0.5.0/tests/test_line_sampling.py +140 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/tests/test_report_audit.py +924 -12
- codegreen-0.5.0/tests/test_sampling.py +141 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/tests/test_timeline.py +15 -0
- codegreen-0.4.9/benchmark/validation/experiments.py +0 -247
- codegreen-0.4.9/codegreen/analysis/sampling.py +0 -157
- codegreen-0.4.9/codegreen/config.json +0 -262
- codegreen-0.4.9/codegreen/instrumentation/config.py +0 -73
- codegreen-0.4.9/codegreen/measurement/src/nemb/config_loader.cpp +0 -313
- codegreen-0.4.9/codegreen/measurement/src/nemb/core/energy_provider.cpp +0 -91
- codegreen-0.4.9/codegreen/measurement/tests/nemb_validation_test.cpp +0 -383
- codegreen-0.4.9/config/codegreen.json +0 -177
- codegreen-0.4.9/tests/test_sampling.py +0 -72
- {codegreen-0.4.9 → codegreen-0.5.0}/LICENSE +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/__init__.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/_workload_gpt2.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/_workload_gpt2_session.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/_workload_gpt2_session_staged.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/cg_modes_compare.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/compilers.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/suites/__init__.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/suites/base.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/suites/dacapo.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/suites/perfopt.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/suites/renaissance.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/validation/__init__.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/validation/analysis.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/benchmark/validation/reporting.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/__init__.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/_ts_java.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/cfg/__init__.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/cfg/builder.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/cfg/callgraph.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/cfg/dataflow.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/cfg/energy_flow.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/cfg/features.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/cfg/pdg.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/cfg/types.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/cfg/visualization.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/efg/__init__.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/efg/adapter.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/efg/example/Sample.java +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/efg/example/__init__.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/efg/example/demo.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/efg/graph.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/efg/heuristics.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/efg/paths.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/efg/serialize.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/efg/types.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analysis/timeline.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analyzer/__init__.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/analyzer/plot.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/cli/__init__.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/cli/entrypoint.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/ast_processor.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/brace_normaliser.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/bridge_analyze.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/bridge_instrument.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/engine.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/language_configs.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/language_runtimes/cpp/codegreen/runtime.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/language_runtimes/java/codegreen/runtime/CodeGreenRuntime.java +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/language_runtimes/python/codegreen_otlp_sink.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/instrumentation/language_runtimes/python/codegreen_prometheus_sink.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/adapters/language_adapter.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/adapters/python_bridge_adapter.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/config.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/energy_code_mapper.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/energy_storage.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/measurement.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/measurement_engine.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/measurement_session.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/accuracy_validator.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/drivers/amd_rapl_provider.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/drivers/arm_energy_provider.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/drivers/darwin_ioreport_provider.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/drivers/darwin_kpc_provider.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/drivers/windows_emi_provider.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/hal/counter_manager.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/utils/non_blocking_file_reader.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/include/nemb/utils/precision_timer.hpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/energy_code_mapper.cpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/accuracy_validator.cpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/drivers/arm_energy_provider.cpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/drivers/darwin_ioreport_provider.cpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/drivers/darwin_kpc_provider.cpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/hal/counter_manager.cpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/utils/non_blocking_file_reader.cpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/src/nemb/utils/precision_timer.cpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/measurement/tests/rapl_provider_test.cpp +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/utils/__init__.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/utils/binary.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen/utils/platform.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen.egg-info/dependency_links.txt +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen.egg-info/entry_points.txt +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen.egg-info/not-zip-safe +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen.egg-info/requires.txt +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/codegreen.egg-info/top_level.txt +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/requirements.txt +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/setup.cfg +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/tests/test_callgraph_python.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/tests/test_efg.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/tests/test_examples.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/tests/test_hotspot_taxonomy.py +0 -0
- {codegreen-0.4.9 → codegreen-0.5.0}/tests/test_instrumentation_braces.py +0 -0
|
@@ -46,15 +46,8 @@ include_directories(${CMAKE_SOURCE_DIR}/codegreen/measurement/include)
|
|
|
46
46
|
add_subdirectory(codegreen/measurement)
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
#
|
|
50
|
-
|
|
51
|
-
file(MAKE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/config)
|
|
52
|
-
configure_file(
|
|
53
|
-
${CMAKE_SOURCE_DIR}/config/codegreen.json
|
|
54
|
-
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/config/codegreen.json
|
|
55
|
-
COPYONLY
|
|
56
|
-
)
|
|
57
|
-
endif()
|
|
49
|
+
# NEMB finds its config at <library dir>/../config.json; place the bundled one there so the build tree works
|
|
50
|
+
configure_file(${CMAKE_SOURCE_DIR}/codegreen/config.json ${CMAKE_BINARY_DIR}/config.json COPYONLY)
|
|
58
51
|
|
|
59
52
|
# Dev-install: copy NEMB library to project root for pip editable installs
|
|
60
53
|
add_custom_target(dev-install ALL
|
|
@@ -73,17 +66,13 @@ install(TARGETS codegreen-nemb
|
|
|
73
66
|
RUNTIME DESTINATION lib
|
|
74
67
|
)
|
|
75
68
|
|
|
69
|
+
install(FILES ${CMAKE_SOURCE_DIR}/codegreen/config.json DESTINATION share/codegreen) # <prefix>/lib/../share/codegreen
|
|
70
|
+
|
|
76
71
|
install(FILES
|
|
77
72
|
${CMAKE_SOURCE_DIR}/codegreen/instrumentation/language_runtimes/python/codegreen_runtime.py
|
|
78
73
|
DESTINATION bin/runtime
|
|
79
74
|
)
|
|
80
75
|
|
|
81
|
-
if(EXISTS "${CMAKE_SOURCE_DIR}/config/codegreen.json")
|
|
82
|
-
install(FILES ${CMAKE_SOURCE_DIR}/config/codegreen.json
|
|
83
|
-
DESTINATION bin/config
|
|
84
|
-
)
|
|
85
|
-
endif()
|
|
86
|
-
|
|
87
76
|
set(CPACK_PACKAGE_NAME "CodeGreen")
|
|
88
77
|
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
|
|
89
78
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Energy-aware software development tool")
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
include README.md LICENSE requirements.txt pyproject.toml CMakeLists.txt setup.py
|
|
2
|
-
include config/codegreen.json
|
|
3
2
|
|
|
4
3
|
recursive-include codegreen/measurement CMakeLists.txt *.cpp *.c *.h *.hpp
|
|
5
4
|
recursive-include codegreen/instrumentation/language_runtimes *.py *.h *.hpp *.java
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codegreen
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Know the true energy cost of your code
|
|
5
5
|
Author-email: Saurabhsingh Rajput <saurabh@dal.ca>
|
|
6
6
|
Maintainer-email: Saurabhsingh Rajput <saurabh@dal.ca>
|
|
@@ -637,7 +637,7 @@ See `codegreen/analysis/efg/README.md` for the module layout, a runnable example
|
|
|
637
637
|
```bash
|
|
638
638
|
# Fail pipeline if energy exceeds budget
|
|
639
639
|
codegreen run --budget 10.0 --json -- python tests/benchmark.py
|
|
640
|
-
# Exit code 1 if mean energy > 10 Joules
|
|
640
|
+
# Exit code 1 if mean energy > 10 Joules, 3 if the measurement is invalid (see validity.reasons)
|
|
641
641
|
```
|
|
642
642
|
|
|
643
643
|
## Upgrading
|
|
@@ -192,7 +192,7 @@ See `codegreen/analysis/efg/README.md` for the module layout, a runnable example
|
|
|
192
192
|
```bash
|
|
193
193
|
# Fail pipeline if energy exceeds budget
|
|
194
194
|
codegreen run --budget 10.0 --json -- python tests/benchmark.py
|
|
195
|
-
# Exit code 1 if mean energy > 10 Joules
|
|
195
|
+
# Exit code 1 if mean energy > 10 Joules, 3 if the measurement is invalid (see validity.reasons)
|
|
196
196
|
```
|
|
197
197
|
|
|
198
198
|
## Upgrading
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""Entry point for `python3 -m benchmark`."""
|
|
2
2
|
import argparse
|
|
3
|
-
import sys
|
|
4
3
|
from pathlib import Path
|
|
5
4
|
from benchmark.config import BenchmarkConfig
|
|
6
5
|
from benchmark.harness import BenchmarkHarness
|
|
6
|
+
from benchmark.suites.benchmarksgame import BenchmarksgameSuite
|
|
7
7
|
|
|
8
8
|
def main():
|
|
9
9
|
parser = argparse.ArgumentParser(description="CodeGreen Energy Benchmark Suite")
|
|
@@ -18,27 +18,23 @@ def main():
|
|
|
18
18
|
args = parser.parse_args()
|
|
19
19
|
|
|
20
20
|
config = BenchmarkConfig.from_yaml(args.config) if args.config else BenchmarkConfig.default()
|
|
21
|
-
|
|
21
|
+
suite = BenchmarksgameSuite(Path(config.benchmarks_dir) if config.benchmarks_dir else None,
|
|
22
|
+
languages=args.languages, problems=args.problems, sizes=args.sizes)
|
|
23
|
+
harness = BenchmarkHarness(suite, repetitions=args.repetitions, warmup_runs=config.warmup_runs,
|
|
24
|
+
timeout_seconds=config.timeout_seconds, min_runtime_seconds=config.min_runtime_seconds)
|
|
22
25
|
|
|
23
26
|
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
|
-
)
|
|
27
|
+
collector = harness.run_suite(profilers=args.profilers, repetitions=args.repetitions)
|
|
31
28
|
summaries = collector.summarize_all()
|
|
32
29
|
print(f"\n{'='*60}")
|
|
33
30
|
print(f"Results: {len(collector.results)} runs")
|
|
34
31
|
print(f"{'='*60}")
|
|
35
32
|
for key, data in sorted(summaries.items()):
|
|
36
|
-
e = data
|
|
37
|
-
t = data.get("time")
|
|
38
|
-
valid = data.get("valid_runs", 0)
|
|
39
|
-
total = data.get("total_runs", 0)
|
|
33
|
+
e, t = data["energy"], data["time"]
|
|
40
34
|
energy_str = f"{e.mean:.4f} +/- {e.std:.4f} J" if e else "N/A"
|
|
41
|
-
|
|
35
|
+
time_str = f"{t.mean:.3f}s" if t else "N/A"
|
|
36
|
+
print(f" {key}: energy={energy_str} time={time_str} valid={data['valid_runs']}/{data['total_runs']}"
|
|
37
|
+
f" invalid={data['invalid_reasons']}")
|
|
42
38
|
|
|
43
39
|
if args.output:
|
|
44
40
|
collector.to_json(args.output)
|
|
@@ -15,7 +15,7 @@ class SystemState:
|
|
|
15
15
|
cpu_model: str = ""
|
|
16
16
|
cpu_governor: str = ""
|
|
17
17
|
kernel_version: str = ""
|
|
18
|
-
|
|
18
|
+
power_events: List[str] = field(default_factory=list)
|
|
19
19
|
|
|
20
20
|
@classmethod
|
|
21
21
|
def capture(cls) -> "SystemState":
|
|
@@ -34,17 +34,8 @@ class SystemState:
|
|
|
34
34
|
state.cpu_governor = f.read().strip()
|
|
35
35
|
except (FileNotFoundError, OSError):
|
|
36
36
|
pass
|
|
37
|
-
import
|
|
38
|
-
|
|
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
|
|
37
|
+
from benchmark.profilers import perf_power_events
|
|
38
|
+
state.power_events = perf_power_events()
|
|
48
39
|
return state
|
|
49
40
|
|
|
50
41
|
@dataclass
|
|
@@ -66,7 +57,7 @@ class RunResult:
|
|
|
66
57
|
language: str
|
|
67
58
|
size: str
|
|
68
59
|
profiler: str
|
|
69
|
-
energy_joules: float
|
|
60
|
+
energy_joules: Optional[float]
|
|
70
61
|
time_seconds: float
|
|
71
62
|
output_valid: bool
|
|
72
63
|
repetition: int = 0
|
|
@@ -74,6 +65,31 @@ class RunResult:
|
|
|
74
65
|
suite: str = "benchmarksgame"
|
|
75
66
|
timestamp: datetime = field(default_factory=datetime.now)
|
|
76
67
|
checkpoints: List[Dict[str, Any]] = field(default_factory=list)
|
|
68
|
+
validity: Optional[Dict[str, Any]] = None
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def measurement_valid(self) -> bool:
|
|
72
|
+
"""No fatal validity reason; profilers without a validity contract (perf, native) report None."""
|
|
73
|
+
return self.validity is None or self.validity["status"] == "valid"
|
|
74
|
+
|
|
75
|
+
# perf power events summed as the CPU-scope truth, mirroring the RAPL adapters' top-level domain rule: the first set
|
|
76
|
+
# whose first event the power PMU offers wins (psys supersedes package + dram), using the offered events of that set.
|
|
77
|
+
PERF_CPU_EVENT_SETS = (("energy-psys",), ("energy-pkg", "energy-ram"))
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class ValidityTolerances:
|
|
81
|
+
"""Expectations of benchmark.validation.experiments.ValidityExperiment. Windows are in sampler intervals
|
|
82
|
+
(sampler.interval_ms of the run itself); a window below min_window_samples - 1 intervals must be flagged
|
|
83
|
+
TOTAL_BELOW_RESOLUTION, one at or above min_window_samples + 1 must not, the two intervals between are either (one
|
|
84
|
+
late reading costs the window a reading). A sampler allowed on fewer than min_affinity_cpus CPUs must raise the
|
|
85
|
+
AFFINITY_SINGLE_CPU warning and may be SAMPLER_STARVED, as may any run whose reading gaps exceed codegreen's gap policy.
|
|
86
|
+
cv_max_pct maps a minimum window (s) to the largest CV (%) of valid energies across repetitions."""
|
|
87
|
+
min_window_samples: int = 2
|
|
88
|
+
min_affinity_cpus: int = 2
|
|
89
|
+
exclusive_rel_tol: float = 0.01
|
|
90
|
+
run_vs_perf_rel_tol: float = 0.03
|
|
91
|
+
run_vs_perf_min_duration_s: float = 1.0
|
|
92
|
+
cv_max_pct: Dict[float, float] = field(default_factory=lambda: {1.0: 5.0, 0.1: 10.0})
|
|
77
93
|
|
|
78
94
|
@dataclass
|
|
79
95
|
class BenchmarkConfig:
|
|
@@ -3,7 +3,6 @@ import math
|
|
|
3
3
|
import subprocess
|
|
4
4
|
import sys
|
|
5
5
|
import time
|
|
6
|
-
from pathlib import Path
|
|
7
6
|
from typing import Callable, List, Optional
|
|
8
7
|
|
|
9
8
|
from benchmark.config import RunResult
|
|
@@ -49,11 +48,13 @@ class BenchmarkHarness:
|
|
|
49
48
|
"""Run a single task with a single profiler."""
|
|
50
49
|
reps = repetitions or self.repetitions
|
|
51
50
|
profiler = get_profiler(profiler_name)
|
|
51
|
+
if not profiler.is_available():
|
|
52
|
+
self.progress_callback(f" Skipping {task.name} [{profiler_name}]: {profiler.unavailable_reason()}")
|
|
53
|
+
return []
|
|
54
|
+
# Build the task (compile, construct run command); the build may name the source anew (Java public class)
|
|
55
|
+
task = self.suite.build(task)
|
|
52
56
|
if hasattr(profiler, 'set_source') and task.source_file:
|
|
53
57
|
profiler.set_source(task.source_file, task.language)
|
|
54
|
-
|
|
55
|
-
# Build the task (compile, construct run command)
|
|
56
|
-
task = self.suite.build(task)
|
|
57
58
|
if not task.run_command:
|
|
58
59
|
self.progress_callback(f" No run command for {task.name}, skipping")
|
|
59
60
|
return []
|
|
@@ -96,6 +97,7 @@ class BenchmarkHarness:
|
|
|
96
97
|
variant=task.variant,
|
|
97
98
|
suite=self.suite.name,
|
|
98
99
|
checkpoints=profile_result.checkpoints,
|
|
100
|
+
validity=profile_result.validity,
|
|
99
101
|
)
|
|
100
102
|
results.append(result)
|
|
101
103
|
self.collector.add(result)
|
|
@@ -105,6 +107,22 @@ class BenchmarkHarness:
|
|
|
105
107
|
self.progress_callback(f" Run {i+1} failed: {e}")
|
|
106
108
|
return results
|
|
107
109
|
|
|
110
|
+
def tasks(self, problem: str, languages: List[str], sizes: Optional[List[str]] = None) -> List[Task]:
|
|
111
|
+
"""Tasks of one problem at the given languages and sizes (suite defaults when None); the default suite is
|
|
112
|
+
benchmarksgame."""
|
|
113
|
+
if self.suite is None:
|
|
114
|
+
from benchmark.suites.benchmarksgame import BenchmarksgameSuite
|
|
115
|
+
self.suite = BenchmarksgameSuite()
|
|
116
|
+
found = self.suite.discover({"problems": [problem], "languages": languages, "sizes": sizes})
|
|
117
|
+
if not found:
|
|
118
|
+
raise ValueError(f"{self.suite.name} has no task for {problem} in {languages} at sizes {sizes}")
|
|
119
|
+
return found
|
|
120
|
+
|
|
121
|
+
def run_problem(self, problem: str, languages: List[str], sizes: List[str], profilers: List[str],
|
|
122
|
+
repetitions: Optional[int] = None) -> List[RunResult]:
|
|
123
|
+
"""Run one problem for every language, size and profiler; returns the results of all runs."""
|
|
124
|
+
return [r for t in self.tasks(problem, languages, sizes) for p in profilers for r in self.run_task(t, p, repetitions)]
|
|
125
|
+
|
|
108
126
|
def run_suite(self, profilers: Optional[List[str]] = None,
|
|
109
127
|
repetitions: Optional[int] = None,
|
|
110
128
|
filters: Optional[dict] = None) -> ResultCollector:
|
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
"""Energy profiler interfaces for CodeGreen and perf RAPL."""
|
|
2
2
|
import json
|
|
3
|
+
import os
|
|
3
4
|
import re
|
|
5
|
+
import shlex
|
|
6
|
+
import shutil
|
|
4
7
|
import subprocess
|
|
5
8
|
import tempfile
|
|
6
9
|
import time
|
|
7
10
|
from abc import ABC, abstractmethod
|
|
8
11
|
from dataclasses import dataclass
|
|
9
12
|
from pathlib import Path
|
|
10
|
-
from typing import Any, Dict, List
|
|
13
|
+
from typing import Any, Dict, List, Optional
|
|
14
|
+
|
|
15
|
+
from benchmark.config import PERF_CPU_EVENT_SETS
|
|
11
16
|
|
|
12
17
|
@dataclass
|
|
13
18
|
class ProfileResult:
|
|
14
|
-
energy_joules: float
|
|
19
|
+
energy_joules: Optional[float]
|
|
15
20
|
time_seconds: float
|
|
16
21
|
output: str
|
|
17
22
|
checkpoints: List[Dict[str, Any]]
|
|
18
23
|
raw_data: Dict[str, Any]
|
|
24
|
+
validity: Optional[Dict[str, Any]] = None
|
|
19
25
|
|
|
20
26
|
class ProfilerInterface(ABC):
|
|
21
27
|
@abstractmethod
|
|
@@ -26,11 +32,22 @@ class ProfilerInterface(ABC):
|
|
|
26
32
|
def is_available(self) -> bool:
|
|
27
33
|
pass
|
|
28
34
|
|
|
35
|
+
def unavailable_reason(self) -> str:
|
|
36
|
+
return f"{type(self).__name__} is not available on this host"
|
|
37
|
+
|
|
38
|
+
def perf_power_events() -> List[str]:
|
|
39
|
+
"""RAPL events the kernel power PMU offers through perf (empty without perf or without a power PMU)."""
|
|
40
|
+
if not shutil.which("perf"):
|
|
41
|
+
return []
|
|
42
|
+
out = subprocess.run(["perf", "list", "power"], capture_output=True, text=True, timeout=60).stdout
|
|
43
|
+
return sorted(set(re.findall(r"power/(energy-[\w-]+)/", out)))
|
|
44
|
+
|
|
29
45
|
class CodeGreenProfiler(ProfilerInterface):
|
|
30
46
|
"""Uses codegreen measure CLI for energy measurement."""
|
|
31
47
|
def __init__(self):
|
|
32
48
|
self.source_path: Path | None = None
|
|
33
49
|
self.language: str | None = None
|
|
50
|
+
self.extra_args: List[str] = []
|
|
34
51
|
|
|
35
52
|
def set_source(self, source: Path, language: str = None):
|
|
36
53
|
"""Set source file and language for measurement."""
|
|
@@ -46,25 +63,31 @@ class CodeGreenProfiler(ProfilerInterface):
|
|
|
46
63
|
if cmd[0] == "python3":
|
|
47
64
|
args = cmd[2:] if len(cmd) > 2 else []
|
|
48
65
|
elif cmd[0] == "java":
|
|
49
|
-
args = cmd[
|
|
66
|
+
args = cmd[4:] # java -cp dir class args
|
|
50
67
|
else:
|
|
51
68
|
args = cmd[1:] if len(cmd) > 1 else [] # binary args
|
|
52
69
|
else:
|
|
53
70
|
# Fallback: detect from command
|
|
54
71
|
lang, source, args = self._detect_from_cmd(cmd)
|
|
55
72
|
|
|
56
|
-
|
|
57
|
-
full_cmd = ["codegreen", "measure", lang, source, "--json", "--timeout", str(timeout)] + args
|
|
73
|
+
full_cmd = ["codegreen", "measure", lang, source, "--json", "--timeout", str(timeout), *self.extra_args] + args
|
|
58
74
|
env = {**os.environ, "PYTHONUNBUFFERED": "1"}
|
|
59
75
|
|
|
60
76
|
start = time.perf_counter()
|
|
61
77
|
result = subprocess.run(full_cmd, capture_output=True, text=True, timeout=timeout + 30, env=env)
|
|
62
78
|
elapsed = time.perf_counter() - start
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
# The JSON result is printed on every exit code (exit 3 = invalid measurement); signed energy is kept as reported.
|
|
80
|
+
try:
|
|
81
|
+
raw = json.loads(result.stdout)
|
|
82
|
+
except json.JSONDecodeError:
|
|
83
|
+
raise RuntimeError(f"codegreen measure exit {result.returncode} without a JSON result: {result.stderr[-500:]}")
|
|
84
|
+
validity = raw.get("validity")
|
|
85
|
+
if validity is None or result.returncode not in (0, 3):
|
|
86
|
+
detail = {**(validity or {}).get("detail", {}),
|
|
87
|
+
"MEASURE_FAILED": raw.get("error") or f"codegreen measure exit {result.returncode}"}
|
|
88
|
+
validity = {**(validity or {"warnings": {}}), "status": "invalid", "reasons": list(detail), "detail": detail}
|
|
89
|
+
return ProfileResult((raw.get("summary") or {}).get("total_energy_j"), elapsed, "",
|
|
90
|
+
raw.get("checkpoints") or [], raw, validity)
|
|
68
91
|
|
|
69
92
|
def _detect_from_cmd(self, cmd: List[str]) -> tuple:
|
|
70
93
|
"""Fallback: detect language, source, args from command."""
|
|
@@ -72,118 +95,56 @@ class CodeGreenProfiler(ProfilerInterface):
|
|
|
72
95
|
return "python", cmd[1] if len(cmd) > 1 else cmd[0], cmd[2:] if len(cmd) > 2 else []
|
|
73
96
|
elif cmd[0] == "java":
|
|
74
97
|
# java -cp dir class args -> need source from set_source
|
|
75
|
-
return "java", str(self.source_path) if self.source_path else "", cmd[
|
|
98
|
+
return "java", str(self.source_path) if self.source_path else "", cmd[4:]
|
|
76
99
|
else:
|
|
77
100
|
# Compiled binary
|
|
78
101
|
source = str(self.source_path) if self.source_path else cmd[0]
|
|
79
102
|
lang = "cpp" if (".gpp" in source or "cpp" in source) else "c"
|
|
80
103
|
return lang, source, cmd[1:] if len(cmd) > 1 else []
|
|
81
104
|
|
|
82
|
-
def _parse_output(self, stdout: str) -> tuple:
|
|
83
|
-
energy, checkpoints, raw, program_output = 0.0, [], {}, ""
|
|
84
|
-
try:
|
|
85
|
-
raw = json.loads(stdout)
|
|
86
|
-
except (json.JSONDecodeError, ValueError):
|
|
87
|
-
return energy, checkpoints, raw, stdout
|
|
88
|
-
|
|
89
|
-
# Extract energy from summary (primary) or checkpoints (fallback)
|
|
90
|
-
summary = raw.get("summary", {})
|
|
91
|
-
if summary.get("total_energy_j", 0) > 0:
|
|
92
|
-
energy = summary["total_energy_j"]
|
|
93
|
-
|
|
94
|
-
checkpoints = raw.get("checkpoints", [])
|
|
95
|
-
if energy <= 0 and len(checkpoints) >= 2:
|
|
96
|
-
first_j = checkpoints[0].get("joules", 0)
|
|
97
|
-
last_j = checkpoints[-1].get("joules", 0)
|
|
98
|
-
energy = last_j - first_j
|
|
99
|
-
if energy < 0:
|
|
100
|
-
energy = 0.0
|
|
101
|
-
|
|
102
|
-
return energy, checkpoints, raw, program_output
|
|
103
|
-
|
|
104
105
|
def is_available(self) -> bool:
|
|
105
|
-
|
|
106
|
-
with open("/sys/class/powercap/intel-rapl:0/energy_uj") as f:
|
|
107
|
-
f.read()
|
|
108
|
-
return True
|
|
109
|
-
except (FileNotFoundError, PermissionError, OSError):
|
|
110
|
-
return False
|
|
106
|
+
return shutil.which("codegreen") is not None
|
|
111
107
|
|
|
112
108
|
class PerfProfiler(ProfilerInterface):
|
|
109
|
+
"""perf stat -a over the command: system-wide RAPL energy at the CPU domain set of PERF_CPU_EVENT_SETS, the
|
|
110
|
+
truth source for `codegreen run/measure --sensors rapl`."""
|
|
113
111
|
def __init__(self):
|
|
114
|
-
|
|
115
|
-
self.
|
|
112
|
+
offered = perf_power_events()
|
|
113
|
+
self.events = next(([e for e in s if e in offered] for s in PERF_CPU_EVENT_SETS if s[0] in offered), [])
|
|
116
114
|
self.repeat_count: int = 1
|
|
117
115
|
|
|
118
|
-
def __del__(self):
|
|
119
|
-
self._cleanup()
|
|
120
|
-
|
|
121
|
-
def _detect_events(self) -> str:
|
|
122
|
-
try:
|
|
123
|
-
result = subprocess.run(["perf", "list", "power"], capture_output=True, text=True, timeout=5)
|
|
124
|
-
events = []
|
|
125
|
-
if "energy-pkg" in result.stdout:
|
|
126
|
-
events.append("power/energy-pkg/")
|
|
127
|
-
if "energy-ram" in result.stdout:
|
|
128
|
-
events.append("power/energy-ram/")
|
|
129
|
-
return ",".join(events) if events else "power/energy-pkg/"
|
|
130
|
-
except Exception:
|
|
131
|
-
return "power/energy-pkg/"
|
|
132
|
-
|
|
133
116
|
def run(self, cmd: List[str], timeout: int = 300) -> ProfileResult:
|
|
134
|
-
|
|
135
|
-
self.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
def _parse_perf_output(self) -> float:
|
|
161
|
-
if not self.perf_output_file or not self.perf_output_file.exists():
|
|
162
|
-
return 0.0
|
|
163
|
-
content = self.perf_output_file.read_text()
|
|
164
|
-
total = 0.0
|
|
165
|
-
pkg_match = re.search(r'([\d.,]+)\s+Joules\s+power/energy-pkg/', content)
|
|
166
|
-
if pkg_match:
|
|
167
|
-
total += float(pkg_match.group(1).replace(',', ''))
|
|
168
|
-
ram_match = re.search(r'([\d.,]+)\s+Joules\s+power/energy-ram/', content)
|
|
169
|
-
if ram_match:
|
|
170
|
-
total += float(ram_match.group(1).replace(',', ''))
|
|
171
|
-
return total
|
|
172
|
-
|
|
173
|
-
def _cleanup(self):
|
|
174
|
-
if self.perf_output_file and self.perf_output_file.exists():
|
|
175
|
-
try:
|
|
176
|
-
self.perf_output_file.unlink()
|
|
177
|
-
except OSError:
|
|
178
|
-
pass
|
|
179
|
-
self.perf_output_file = None
|
|
117
|
+
body = cmd if self.repeat_count == 1 else [
|
|
118
|
+
"bash", "-c", f"for i in $(seq {self.repeat_count}); do {shlex.join(cmd)}; done"]
|
|
119
|
+
with tempfile.NamedTemporaryFile(suffix=".csv", delete=False) as f:
|
|
120
|
+
out = Path(f.name)
|
|
121
|
+
try:
|
|
122
|
+
start = time.perf_counter()
|
|
123
|
+
result = subprocess.run(["perf", "stat", "-a", "-x,", "-e", ",".join(f"power/{e}/" for e in self.events),
|
|
124
|
+
"-o", str(out), "--", *body], capture_output=True, text=True, timeout=timeout)
|
|
125
|
+
elapsed = time.perf_counter() - start
|
|
126
|
+
text = out.read_text()
|
|
127
|
+
finally:
|
|
128
|
+
out.unlink()
|
|
129
|
+
if result.returncode:
|
|
130
|
+
raise RuntimeError(f"perf stat exit {result.returncode}: {result.stderr[-500:]}")
|
|
131
|
+
events = {}
|
|
132
|
+
for f in (line.split(",") for line in text.splitlines()):
|
|
133
|
+
if len(f) > 2 and f[2].startswith("power/"):
|
|
134
|
+
if not f[0][:1].isdigit():
|
|
135
|
+
raise RuntimeError(f"perf could not count {f[2]}: {f[0]}")
|
|
136
|
+
events[f[2][6:-1]] = float(f[0]) / self.repeat_count
|
|
137
|
+
if sorted(events) != sorted(self.events):
|
|
138
|
+
raise RuntimeError(f"perf counted {sorted(events)}, expected {self.events}")
|
|
139
|
+
return ProfileResult(sum(events.values()), elapsed / self.repeat_count, result.stdout, [],
|
|
140
|
+
{"events_j": events, "repeat_count": self.repeat_count})
|
|
180
141
|
|
|
181
142
|
def is_available(self) -> bool:
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
143
|
+
return bool(self.events)
|
|
144
|
+
|
|
145
|
+
def unavailable_reason(self) -> str:
|
|
146
|
+
return (f"perf offers none of the power events {PERF_CPU_EVENT_SETS} (`perf list power`: "
|
|
147
|
+
f"{perf_power_events() or 'none'}); install perf on a host with a RAPL power PMU")
|
|
187
148
|
|
|
188
149
|
class NativeProfiler(ProfilerInterface):
|
|
189
150
|
"""Runs without energy profiling - for baseline timing."""
|
|
@@ -192,7 +153,7 @@ class NativeProfiler(ProfilerInterface):
|
|
|
192
153
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
|
|
193
154
|
elapsed = time.perf_counter() - start
|
|
194
155
|
return ProfileResult(
|
|
195
|
-
energy_joules=
|
|
156
|
+
energy_joules=None,
|
|
196
157
|
time_seconds=elapsed,
|
|
197
158
|
output=result.stdout,
|
|
198
159
|
checkpoints=[],
|
|
@@ -208,7 +169,7 @@ class CodeCarbonProfiler(ProfilerInterface):
|
|
|
208
169
|
try:
|
|
209
170
|
from codecarbon import EmissionsTracker
|
|
210
171
|
except ImportError:
|
|
211
|
-
|
|
172
|
+
raise RuntimeError("codecarbon not installed")
|
|
212
173
|
tracker = EmissionsTracker(log_level="error", save_to_file=False)
|
|
213
174
|
tracker.start()
|
|
214
175
|
start = time.perf_counter()
|
|
@@ -220,11 +181,8 @@ class CodeCarbonProfiler(ProfilerInterface):
|
|
|
220
181
|
{"emissions_kg_co2": emissions, "energy_kwh": tracker._total_energy.kWh})
|
|
221
182
|
|
|
222
183
|
def is_available(self) -> bool:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
return True
|
|
226
|
-
except ImportError:
|
|
227
|
-
return False
|
|
184
|
+
import importlib.util
|
|
185
|
+
return importlib.util.find_spec("codecarbon") is not None
|
|
228
186
|
|
|
229
187
|
|
|
230
188
|
class JoularJXProfiler(ProfilerInterface):
|
|
@@ -240,7 +198,7 @@ class JoularJXProfiler(ProfilerInterface):
|
|
|
240
198
|
|
|
241
199
|
def run(self, cmd: List[str], timeout: int = 300) -> ProfileResult:
|
|
242
200
|
if not self.agent_path:
|
|
243
|
-
|
|
201
|
+
raise RuntimeError("JoularJX agent not found")
|
|
244
202
|
jx_cmd = cmd.copy()
|
|
245
203
|
if cmd[0] == "java":
|
|
246
204
|
jx_cmd.insert(1, f"-javaagent:{self.agent_path}")
|
|
@@ -253,7 +211,7 @@ class JoularJXProfiler(ProfilerInterface):
|
|
|
253
211
|
def _parse_joularjx_output(self) -> float:
|
|
254
212
|
csv_path = Path("joularJX-all-methods-energy.csv")
|
|
255
213
|
if not csv_path.exists():
|
|
256
|
-
|
|
214
|
+
raise RuntimeError(f"JoularJX wrote no {csv_path}")
|
|
257
215
|
total = 0.0
|
|
258
216
|
for line in csv_path.read_text().splitlines()[1:]:
|
|
259
217
|
parts = line.split(",")
|