File indexing completed on 2024-07-31 22:58:26
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include "FWCore/MessageLogger/interface/JobReport.h"
0012 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0013 #include "FWCore/ServiceRegistry/interface/ActivityRegistry.h"
0014 #include "FWCore/ServiceRegistry/interface/Service.h"
0015 #include "FWCore/ParameterSet/interface/ParameterSet.h"
0016 #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
0017 #include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
0018 #include "FWCore/Utilities/interface/CPUServiceBase.h"
0019 #include "FWCore/Utilities/interface/ResourceInformation.h"
0020
0021 #include "cpu_features/cpu_features_macros.h"
0022
0023 #if defined(CPU_FEATURES_ARCH_X86)
0024 #include "cpu_features/cpuinfo_x86.h"
0025 #elif defined(CPU_FEATURES_ARCH_ARM)
0026 #include "cpu_features/cpuinfo_arm.h"
0027 #elif defined(CPU_FEATURES_ARCH_AARCH64)
0028 #include "cpu_features/cpuinfo_aarch64.h"
0029 #elif defined(CPU_FEATURES_ARCH_PPC)
0030 #include "cpu_features/cpuinfo_ppc.h"
0031 #elif defined(CPU_FEATURES_ARCH_RISCV)
0032 #include "cpu_features/cpuinfo_riscv.h"
0033 #endif
0034
0035 #include <cstdlib>
0036 #include <string>
0037 #include <fstream>
0038 #include <sstream>
0039 #include <map>
0040 #include <set>
0041 #include <utility>
0042 #include <vector>
0043 #include <fmt/format.h>
0044
0045 #ifdef __linux__
0046 #include <sched.h>
0047 #include <cerrno>
0048 #endif
0049
0050 namespace edm {
0051 using CPUInfoType = std::vector<std::pair<std::string, std::string>>;
0052
0053 namespace service {
0054 class CPU : public CPUServiceBase {
0055 public:
0056 CPU(ParameterSet const &, ActivityRegistry &);
0057 ~CPU() override = default;
0058
0059 static void fillDescriptions(ConfigurationDescriptions &descriptions);
0060
0061 private:
0062 const bool reportCPUProperties_;
0063 const bool disableJobReportOutput_;
0064
0065 bool parseCPUInfo(CPUInfoType &info) const;
0066 std::vector<std::string> getModels(const CPUInfoType &info) const;
0067 std::string formatModels(const std::vector<std::string> &models) const;
0068 std::string getModelFromCPUFeatures() const;
0069 double getAverageSpeed(const CPUInfoType &info) const;
0070 void postEndJob();
0071 };
0072
0073 inline bool isProcessWideService(CPU const *) { return true; }
0074 }
0075 }
0076
0077 namespace edm {
0078 namespace service {
0079 namespace {
0080
0081 void trim(std::string &s, const std::string &drop = " \t") {
0082 std::string::size_type p = s.find_last_not_of(drop);
0083 if (p != std::string::npos) {
0084 s = s.erase(p + 1);
0085 }
0086 s = s.erase(0, s.find_first_not_of(drop));
0087 }
0088
0089 void compressWhitespace(std::string &s) {
0090 auto last =
0091 std::unique(s.begin(), s.end(), [](const auto a, const auto b) { return std::isspace(a) && a == b; });
0092 s.erase(last, s.end());
0093 }
0094
0095
0096
0097 bool getCpuSetSize(unsigned &set_size) {
0098 #ifdef __linux__
0099 cpu_set_t *cpusetp;
0100 unsigned current_size = 128;
0101 unsigned cpu_count = 0;
0102 while (current_size * 2 > current_size) {
0103 cpusetp = CPU_ALLOC(current_size);
0104 CPU_ZERO_S(CPU_ALLOC_SIZE(current_size), cpusetp);
0105
0106 if (sched_getaffinity(0, CPU_ALLOC_SIZE(current_size), cpusetp)) {
0107 CPU_FREE(cpusetp);
0108 if (errno == EINVAL) {
0109 current_size *= 2;
0110 continue;
0111 }
0112 return false;
0113 }
0114 cpu_count = CPU_COUNT_S(CPU_ALLOC_SIZE(current_size), cpusetp);
0115 CPU_FREE(cpusetp);
0116 break;
0117 }
0118 set_size = cpu_count;
0119 return true;
0120 #else
0121 return false;
0122 #endif
0123 }
0124 }
0125
0126 CPU::CPU(const ParameterSet &iPS, ActivityRegistry &iRegistry)
0127 : reportCPUProperties_(iPS.getUntrackedParameter<bool>("reportCPUProperties")),
0128 disableJobReportOutput_(iPS.getUntrackedParameter<bool>("disableJobReportOutput")) {
0129 edm::Service<edm::ResourceInformation> resourceInformationService;
0130 if (resourceInformationService.isAvailable()) {
0131 CPUInfoType info;
0132 if (parseCPUInfo(info)) {
0133 const auto models{getModels(info)};
0134 resourceInformationService->setCPUModels(models);
0135 resourceInformationService->setCpuModelsFormatted(formatModels(models));
0136 resourceInformationService->setCpuAverageSpeed(getAverageSpeed(info));
0137 }
0138 }
0139 iRegistry.watchPostEndJob(this, &CPU::postEndJob);
0140 }
0141
0142 void CPU::fillDescriptions(edm::ConfigurationDescriptions &descriptions) {
0143 edm::ParameterSetDescription desc;
0144 desc.addUntracked<bool>("reportCPUProperties", false);
0145 desc.addUntracked<bool>("disableJobReportOutput", false);
0146 descriptions.add("CPU", desc);
0147 }
0148
0149 void CPU::postEndJob() {
0150 if (disableJobReportOutput_) {
0151 return;
0152 }
0153
0154 Service<JobReport> reportSvc;
0155
0156 CPUInfoType info;
0157 if (!parseCPUInfo(info)) {
0158 return;
0159 }
0160
0161 const auto models{formatModels(getModels(info))};
0162 unsigned totalNumberCPUs = 0;
0163 std::map<std::string, std::string> currentCoreProperties;
0164 std::string currentCore;
0165
0166 for (const auto &entry : info) {
0167 if (entry.first == "processor") {
0168 if (reportCPUProperties_) {
0169 if (currentCore.empty()) {
0170 currentCore = entry.second;
0171 } else {
0172 reportSvc->reportPerformanceForModule("SystemCPU", "CPU-" + currentCore, currentCoreProperties);
0173 currentCoreProperties.clear();
0174 currentCore = entry.second;
0175 }
0176 }
0177 totalNumberCPUs++;
0178 } else if (reportCPUProperties_) {
0179 currentCoreProperties.insert(entry);
0180 }
0181 }
0182 if (!currentCore.empty() && reportCPUProperties_) {
0183 reportSvc->reportPerformanceForModule("SystemCPU", "CPU-" + currentCore, currentCoreProperties);
0184 }
0185
0186 std::map<std::string, std::string> reportCPUProperties{
0187 {"totalCPUs", std::to_string(totalNumberCPUs)},
0188 {"averageCoreSpeed", std::to_string(getAverageSpeed(info))},
0189 {"CPUModels", models}};
0190 unsigned set_size = -1;
0191 if (getCpuSetSize(set_size)) {
0192 reportCPUProperties.insert(std::make_pair("cpusetCount", std::to_string(set_size)));
0193 }
0194 reportSvc->reportPerformanceSummary("SystemCPU", reportCPUProperties);
0195 }
0196
0197 bool CPU::parseCPUInfo(CPUInfoType &info) const {
0198 info.clear();
0199 std::ifstream fcpuinfo("/proc/cpuinfo");
0200 if (!fcpuinfo.is_open()) {
0201 return false;
0202 }
0203 while (!fcpuinfo.eof()) {
0204 std::string buf;
0205 std::getline(fcpuinfo, buf);
0206
0207 std::istringstream iss(buf);
0208 std::string token;
0209 std::string property;
0210 std::string value;
0211
0212 int time = 1;
0213
0214 while (std::getline(iss, token, ':')) {
0215 switch (time) {
0216 case 1:
0217 property = token;
0218 break;
0219 case 2:
0220 value = token;
0221 break;
0222 default:
0223 value += token;
0224 break;
0225 }
0226 time++;
0227 }
0228 trim(property);
0229 trim(value);
0230 if (property.empty()) {
0231 continue;
0232 }
0233
0234 if (property == "model name") {
0235 compressWhitespace(value);
0236 }
0237 info.emplace_back(property, value);
0238 }
0239 return true;
0240 }
0241
0242 std::string CPU::getModelFromCPUFeatures() const {
0243 using namespace cpu_features;
0244
0245 std::string model;
0246 #if defined(CPU_FEATURES_ARCH_X86)
0247 const auto info{GetX86Info()};
0248 model = info.brand_string;
0249 #elif defined(CPU_FEATURES_ARCH_ARM)
0250 const auto info{GetArmInfo()};
0251 model = fmt::format("ARM {} {} {}", info.implementer, info.architecture, info.variant);
0252 #elif defined(CPU_FEATURES_ARCH_AARCH64)
0253 const auto info{GetAarch64Info()};
0254 model = fmt::format("aarch64 {} {}", info.implementer, info.variant);
0255 #elif defined(CPU_FEATURES_ARCH_PPC)
0256 const auto strings{GetPPCPlatformStrings()};
0257 model = strings.machine;
0258 #elif defined(CPU_FEATURES_ARCH_RISCV)
0259 const auto info{GetRiscvInfo()};
0260 model = fmt::format("riscv64 {} {}", info.vendor, info.uarch);
0261 #endif
0262 return model;
0263 }
0264
0265 std::vector<std::string> CPU::getModels(const CPUInfoType &info) const {
0266 std::set<std::string> modelSet;
0267 for (const auto &entry : info) {
0268 if (entry.first == "model name") {
0269 modelSet.insert(entry.second);
0270 }
0271 }
0272 std::vector<std::string> modelsVector(modelSet.begin(), modelSet.end());
0273
0274
0275 if (modelsVector.empty()) {
0276 modelsVector.emplace_back(getModelFromCPUFeatures());
0277 }
0278 return modelsVector;
0279 }
0280
0281 std::string CPU::formatModels(const std::vector<std::string> &models) const {
0282 std::stringstream ss;
0283 int model = 0;
0284 for (const auto &modelname : models) {
0285 if (model++ != 0) {
0286 ss << ", ";
0287 }
0288 ss << modelname;
0289 }
0290 return ss.str();
0291 }
0292
0293 double CPU::getAverageSpeed(const CPUInfoType &info) const {
0294 double averageCoreSpeed = 0.0;
0295 unsigned coreCount = 0;
0296 for (const auto &entry : info) {
0297 if (entry.first == "cpu MHz") {
0298 try {
0299 averageCoreSpeed += std::stod(entry.second);
0300 } catch (const std::logic_error &e) {
0301 LogWarning("CPU::getAverageSpeed") << "stod(" << entry.second << ") conversion error: " << e.what();
0302 }
0303 coreCount++;
0304 }
0305 }
0306 if (!coreCount) {
0307 return 0;
0308 }
0309 return averageCoreSpeed / static_cast<double>(coreCount);
0310 }
0311 }
0312 }
0313
0314 #include "FWCore/ServiceRegistry/interface/ServiceMaker.h"
0315
0316 using edm::service::CPU;
0317 using CPUMaker = edm::serviceregistry::AllArgsMaker<edm::CPUServiceBase, CPU>;
0318 DEFINE_FWK_SERVICE_MAKER(CPU, CPUMaker);