diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f1f61e8db0725c70b93a0b31e0ff62bba61006a..6927ababefb7b517cbbe3c52696bda52e05834a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,11 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) CACHE STRING "Default build type." FORCE) endif() +#Set CXX Standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + # Set debug print info set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") @@ -96,6 +101,9 @@ add_subdirectory(common) # PMT library add_library(pmt SHARED $<TARGET_OBJECTS:pmt-common>) +#Cray +add_subdirectory(cray) + # PowerSensor if(${BUILD_POWERSENSOR2_PMT}) add_subdirectory(powersensor2) diff --git a/cray/CMakeLists.txt b/cray/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f4a25af350b50a4cd240194c28e5bacc87862c09 --- /dev/null +++ b/cray/CMakeLists.txt @@ -0,0 +1,17 @@ +project(cray) + +add_library(pmt-cray OBJECT Cray.cpp FilenamesHelper.cpp) + +target_link_libraries(pmt-cray Threads::Threads) + +target_include_directories(pmt-cray PRIVATE) + +install(FILES Cray.h DESTINATION include/pmt) + +add_executable(Cray-test Cray-test.cpp) + +target_link_libraries(Cray-test $<TARGET_OBJECTS:pmt-common> pmt-cray) + +install(TARGETS Cray-test RUNTIME DESTINATION bin) + +target_link_libraries(pmt PUBLIC pmt-cray) diff --git a/cray/Cray-test.cpp b/cray/Cray-test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..279cb0222bfbe6cd99b398205e708542a8b108d9 --- /dev/null +++ b/cray/Cray-test.cpp @@ -0,0 +1,8 @@ +#include "../common/pmt-test.h" + +#include "Cray.h" + +int main(int argc, char *argv[]) { + auto sensor = pmt::cray::Cray::Create(); + run(*sensor, argc, argv); +} diff --git a/cray/Cray.cpp b/cray/Cray.cpp new file mode 100644 index 0000000000000000000000000000000000000000..49e5998ac978cd24023a4d2b939e097455e36a89 --- /dev/null +++ b/cray/Cray.cpp @@ -0,0 +1,106 @@ +#include "Cray.h" + +namespace { +double GetPower(const std::string& filePath) { + std::ifstream powerFile(filePath); + if (!powerFile.is_open()) { + throw std::runtime_error("Failed to open power file"); + } + + std::string line; + if (!std::getline(powerFile, line)) { + throw std::runtime_error("Failed to read power value"); + } + + double power; + try { + power = std::stod(line); + } catch (const std::exception& e) { + throw std::runtime_error("Failed to parse power value"); + } + + powerFile.close(); + return power; +} +} // namespace + +namespace pmt::cray { + +class CrayImpl : public Cray { + public: + CrayImpl(); + ~CrayImpl(); + + State GetState() override; + + private: + std::vector<std::string> filenames; + std::string cray_pm_counters_path = "/sys/cray/pm_counters"; + + double previous_timestamp_; + std::vector<CrayMeasurement> previous_measurements_; + + // Mutex used to guard GetMeasurements() + std::mutex mutex_; + + virtual const char* GetDumpFilename() { return "/tmp/pmt_cray.out"; } + + virtual int GetMeasurementInterval() { + return 100; // milliseconds + } + + unsigned int device_number_; + + std::vector<CrayMeasurement> GetMeasurements(); +}; + +std::unique_ptr<Cray> Cray::Create() { return std::make_unique<CrayImpl>(); } + +CrayImpl::CrayImpl() { + filenames = filenames_helper::GetFilenames(cray_pm_counters_path); + filenames = filenames_helper::ReorderFilenames(filenames); +#if defined(DEBUG) + filenames_helper::PrintFilenames(filenames); +#endif + + previous_timestamp_ = GetTime(); + previous_measurements_ = GetMeasurements(); +} + +std::vector<CrayMeasurement> CrayImpl::GetMeasurements() { + std::lock_guard<std::mutex> lock(mutex_); + + std::vector<CrayMeasurement> measurements; + + std::string file_path = ""; + + for (const auto& filename : filenames) { + file_path = cray_pm_counters_path + "/" + filename; + CrayMeasurement measurement; + measurement.name = filename; + measurement.watt = GetPower(file_path); + measurements.push_back(measurement); + } + + return measurements; +} + +State CrayImpl::GetState() { + std::vector<CrayMeasurement> measurements = GetMeasurements(); + State state(measurements.size()); + state.timestamp_ = GetTime(); + + const double duration = (state.timestamp_ - previous_timestamp_); + + for (size_t i = 0; i < measurements.size(); i++) { + state.name_[i] = measurements[i].name; + state.watt_[i] = measurements[i].watt; + const double watt = + (measurements[i].watt + previous_measurements_[i].watt) / 2; + state.joules_[i] += watt * duration; + } + + return state; +} + +} // end namespace pmt::cray diff --git a/cray/Cray.h b/cray/Cray.h new file mode 100644 index 0000000000000000000000000000000000000000..d58e6068ac261b0c0c9e3e2f6a7c540050703a8a --- /dev/null +++ b/cray/Cray.h @@ -0,0 +1,27 @@ +#ifndef CRAY_PMT_H +#define CRAY_PMT_H + +#include <iostream> +#include <string> +#include <sstream> +#include <stdexcept> + +#include "FilenamesHelper.h" +#include "pmt.h" + +namespace pmt { +namespace cray { + +struct CrayMeasurement { + std::string name; + size_t watt; +}; + +class Cray : public PMT { + public: + static std::unique_ptr<Cray> Create(); +}; +} // end namespace cray +} // end namespace pmt + +#endif diff --git a/cray/FilenamesHelper.cpp b/cray/FilenamesHelper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4bc775ad87d01a6cbad75565f7680769b060316a --- /dev/null +++ b/cray/FilenamesHelper.cpp @@ -0,0 +1,105 @@ +#include "FilenamesHelper.h" + +namespace filenames_helper { +void PrintFilenames(std::vector<std::string> filenames) { + for (const auto& filename : filenames) { + std::cout << filename << "\t"; + } + std::cout << std::endl; +} + +std::vector<std::string> GetFilenames(std::string folder_path) { + std::vector<std::string> filenames; + + if (std::filesystem::is_directory(folder_path)) { + for (const auto& entry : std::filesystem::directory_iterator(folder_path)) { + if (std::filesystem::is_regular_file(entry)) { + std::string filename = entry.path().filename().string(); + if (filename.find("power") != std::string::npos && + filename.find("cap") == std::string::npos) { + filenames.push_back(filename); + } + } + } + } else { + std::cerr << "Not a valid directory: " << folder_path << std::endl; + } + + return filenames; +} + +std::vector<std::string> OrderStringsAlphabetically( + const std::vector<std::string>& input_strings) { + std::vector<std::string> ordered_strings = + input_strings; // Make a copy to preserve the original + + // Use the sort function to order the strings alphabetically + std::sort(ordered_strings.begin(), ordered_strings.end()); + + return ordered_strings; +} + +std::vector<std::string> ExtractStringsContainingSubstring( + const std::vector<std::string>& string_vector, + const std::string& substring) { + std::vector<std::string> extracted_strings; + + for (const std::string& str : string_vector) { + if (str.find(substring) != std::string::npos) { + extracted_strings.push_back(str); + } + } + + return extracted_strings; +} + +std::string PopString(std::vector<std::string>& string_vector, + const std::string& string_to_pop) { + auto it = + std::find(string_vector.begin(), string_vector.end(), string_to_pop); + if (it != string_vector.end()) { + std::string popped_value = *it; + string_vector.erase(it); + return popped_value; + } else { + return ""; // Return an empty string if string_to_pop is not found + } +} + +void HandleFilenamesWithSubstring(std::vector<std::string>& ordered_filenames, + std::vector<std::string>& filenames, + const std::string& substring) { + std::vector<std::string> matching_filenames = + ExtractStringsContainingSubstring(filenames, substring); + std::vector<std::string> ordered_matching_filenames = + OrderStringsAlphabetically(matching_filenames); + for (const auto& matching_filename : ordered_matching_filenames) { + ordered_filenames.push_back(PopString(filenames, matching_filename)); + } +} + +std::vector<std::string> ReorderFilenames(std::vector<std::string> filenames) { + std::vector<std::string> ordered_filenames; + + // Handle "power" filenames + std::string tmp_string = PopString(filenames, "power"); + if (!tmp_string.empty()) { + ordered_filenames.push_back(tmp_string); + } + + // Define the substrings to look for and corresponding sorting priority + std::vector<std::string> substrings = {"cpu", "memory", "accel"}; + + // Handle filenames with each substring + for (const auto& substring : substrings) { + HandleFilenamesWithSubstring(ordered_filenames, filenames, substring); + } + + // Add any remaining filenames to the ordered list + for (const auto& filename : filenames) { + ordered_filenames.push_back(filename); + } + + return ordered_filenames; +} +} // end namespace filenames_helper \ No newline at end of file diff --git a/cray/FilenamesHelper.h b/cray/FilenamesHelper.h new file mode 100644 index 0000000000000000000000000000000000000000..54eb7ce22e96ee4b4e1e4640a4839c2fc0fac7b6 --- /dev/null +++ b/cray/FilenamesHelper.h @@ -0,0 +1,22 @@ +#ifndef FILENAMES_HELPER_PMT_H +#define FILENAMES_HELPER_PMT_H + +#include <algorithm> +#include <iostream> +#include <filesystem> +#include <vector> + +namespace filenames_helper { +//For debug only +void PrintFilenames(std::vector<std::string> filenames); + +std::vector<std::string> GetFilenames(std::string folder_path); +std::vector<std::string> OrderStringsAlphabetically(const std::vector<std::string>& input_strings); +std::vector<std::string> ExtractStringsContainingSubstring(const std::vector<std::string>& string_vector, const std::string& substring); +std::string PopString(std::vector<std::string>& string_vector, const std::string& string_to_pop); +void HandleFilenamesWithSubstring(std::vector<std::string>& ordered_filenames, std::vector<std::string>& filenames, const std::string& substring); +std::vector<std::string> ReorderFilenames(std::vector<std::string> filenames); + +} // end namespace filenames_helper + +#endif diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 55ad5caf49c6ed703fb7253da3e93a14559f0164..d9a74116f8b2e8a87f0bb8e45bcf4dc46775d730 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -8,6 +8,7 @@ target_link_libraries( pybind11::lto pybind11::windows_extras $<TARGET_OBJECTS:pmt-common> + pmt-cray pmt-dummy pmt-tegra pmt-rapl diff --git a/python/pmt.py b/python/pmt.py index dfd1d58db1214dfb46f6fec894a02b6daceb901f..fc8481eb38b36415ca084b7adbd1cab7507dfa4b 100644 --- a/python/pmt.py +++ b/python/pmt.py @@ -2,7 +2,9 @@ import pypmt def get_pmt(platform, device_id=0): - if platform in ['powersensor2', 'powersensor3']: + if platform == "cray": + return pypmt.Cray.create() + elif platform in ['powersensor2', 'powersensor3']: try: return pypmt.PowerSensor_.create(device_id, 2) except AttributeError: diff --git a/python/pypmt.cpp b/python/pypmt.cpp index 907cfc9b39e514b65d8b12690ae1ff54d23a83ab..510d2bc9b14a06972faa674b5126a9e28892f06f 100644 --- a/python/pypmt.cpp +++ b/python/pypmt.cpp @@ -2,6 +2,8 @@ #include <../common/pmt.h> +#include <../cray/Cray.h> + #ifdef BUILD_POWERSENSOR2 #include <../powersensor2/PowerSensor2.h> #endif @@ -37,6 +39,12 @@ PYBIND11_MODULE(pypmt, m) { py::class_<pmt::State>(m, "State"); + py::class_<pmt::cray::Cray>(m, "Cray") + .def("create", &pmt::cray::Cray::Create) + .def("read", &pmt::cray::Cray::Read) + .def("startDump", &pmt::cray::Cray::StartDump) + .def("stopDump", &pmt::cray::Cray::StopDump); + #ifdef BUILD_POWERSENSOR2 py::class_<pmt::powersensor2::PowerSensor2>(m, "PowerSensor2") .def("create", &pmt::powersensor2::PowerSensor2::Create)