Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2025-06-17 01:30:26

0001 // -*- C++ -*-
0002 //
0003 // Package:     Services
0004 // Class  :     edm::service::CPU
0005 //
0006 // Implementation:
0007 //
0008 // Original Author:  Natalia Garcia
0009 // CPU.cc: v 1.0 2009/01/08 11:31:07
0010 
0011 #include "FWCore/AbstractServices/interface/CPUServiceBase.h"
0012 #include "FWCore/AbstractServices/interface/ResourceInformation.h"
0013 #include "FWCore/MessageLogger/interface/JobReport.h"
0014 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0015 #include "FWCore/ServiceRegistry/interface/ActivityRegistry.h"
0016 #include "FWCore/ServiceRegistry/interface/Service.h"
0017 #include "FWCore/ParameterSet/interface/ParameterSet.h"
0018 #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
0019 #include "FWCore/ParameterSet/interface/ParameterSetDescription.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   }  // namespace service
0074 }  // namespace edm
0075 
0076 namespace edm {
0077   namespace service {
0078     namespace {
0079 
0080       void trim(std::string &s, const std::string &drop = " \t") {
0081         std::string::size_type p = s.find_last_not_of(drop);
0082         if (p != std::string::npos) {
0083           s = s.erase(p + 1);
0084         }
0085         s = s.erase(0, s.find_first_not_of(drop));
0086       }
0087 
0088       void compressWhitespace(std::string &s) {
0089         auto last =
0090             std::unique(s.begin(), s.end(), [](const auto a, const auto b) { return std::isspace(a) && a == b; });
0091         s.erase(last, s.end());
0092       }
0093 
0094       // Determine the CPU set size; if this can be successfully determined, then this
0095       // returns true.
0096       bool getCpuSetSize(unsigned &set_size) {
0097 #ifdef __linux__
0098         cpu_set_t *cpusetp;
0099         unsigned current_size = 128;
0100         unsigned cpu_count = 0;
0101         while (current_size * 2 > current_size) {
0102           cpusetp = CPU_ALLOC(current_size);
0103           CPU_ZERO_S(CPU_ALLOC_SIZE(current_size), cpusetp);
0104 
0105           if (sched_getaffinity(0, CPU_ALLOC_SIZE(current_size), cpusetp)) {
0106             CPU_FREE(cpusetp);
0107             if (errno == EINVAL) {
0108               current_size *= 2;
0109               continue;
0110             }
0111             return false;
0112           }
0113           cpu_count = CPU_COUNT_S(CPU_ALLOC_SIZE(current_size), cpusetp);
0114           CPU_FREE(cpusetp);
0115           break;
0116         }
0117         set_size = cpu_count;
0118         return true;
0119 #else
0120         return false;
0121 #endif
0122       }
0123     }  // namespace
0124 
0125     CPU::CPU(const ParameterSet &iPS, ActivityRegistry &iRegistry)
0126         : reportCPUProperties_(iPS.getUntrackedParameter<bool>("reportCPUProperties")),
0127           disableJobReportOutput_(iPS.getUntrackedParameter<bool>("disableJobReportOutput")) {
0128       edm::Service<edm::ResourceInformation> resourceInformationService;
0129       if (resourceInformationService.isAvailable()) {
0130         CPUInfoType info;
0131         if (parseCPUInfo(info)) {
0132           const auto models{getModels(info)};
0133           resourceInformationService->setCPUModels(models);
0134           resourceInformationService->setCpuModelsFormatted(formatModels(models));
0135           resourceInformationService->setCpuAverageSpeed(getAverageSpeed(info));
0136         }
0137       }
0138       iRegistry.watchPostEndJob(this, &CPU::postEndJob);
0139     }
0140 
0141     void CPU::fillDescriptions(edm::ConfigurationDescriptions &descriptions) {
0142       edm::ParameterSetDescription desc;
0143       desc.addUntracked<bool>("reportCPUProperties", false);
0144       desc.addUntracked<bool>("disableJobReportOutput", false);
0145       descriptions.add("CPU", desc);
0146     }
0147 
0148     void CPU::postEndJob() {
0149       if (disableJobReportOutput_) {
0150         return;
0151       }
0152 
0153       Service<JobReport> reportSvc;
0154 
0155       CPUInfoType info;
0156       if (!parseCPUInfo(info)) {
0157         return;
0158       }
0159 
0160       const auto models{formatModels(getModels(info))};
0161       unsigned totalNumberCPUs = 0;
0162       std::map<std::string, std::string> currentCoreProperties;
0163       std::string currentCore;
0164 
0165       for (const auto &entry : info) {
0166         if (entry.first == "processor") {
0167           if (reportCPUProperties_) {
0168             if (currentCore.empty()) {  // first core
0169               currentCore = entry.second;
0170             } else {
0171               reportSvc->reportPerformanceForModule("SystemCPU", "CPU-" + currentCore, currentCoreProperties);
0172               currentCoreProperties.clear();
0173               currentCore = entry.second;
0174             }
0175           }
0176           totalNumberCPUs++;
0177         } else if (reportCPUProperties_) {
0178           currentCoreProperties.insert(entry);
0179         }
0180       }
0181       if (!currentCore.empty() && reportCPUProperties_) {
0182         reportSvc->reportPerformanceForModule("SystemCPU", "CPU-" + currentCore, currentCoreProperties);
0183       }
0184 
0185       std::map<std::string, std::string> reportCPUProperties{
0186           {"totalCPUs", std::to_string(totalNumberCPUs)},
0187           {"averageCoreSpeed", std::to_string(getAverageSpeed(info))},
0188           {"CPUModels", models}};
0189       unsigned set_size = -1;
0190       if (getCpuSetSize(set_size)) {
0191         reportCPUProperties.insert(std::make_pair("cpusetCount", std::to_string(set_size)));
0192       }
0193       reportSvc->reportPerformanceSummary("SystemCPU", reportCPUProperties);
0194     }
0195 
0196     bool CPU::parseCPUInfo(CPUInfoType &info) const {
0197       info.clear();
0198       std::ifstream fcpuinfo("/proc/cpuinfo");
0199       if (!fcpuinfo.is_open()) {
0200         return false;
0201       }
0202       while (!fcpuinfo.eof()) {
0203         std::string buf;
0204         std::getline(fcpuinfo, buf);
0205 
0206         std::istringstream iss(buf);
0207         std::string token;
0208         std::string property;
0209         std::string value;
0210 
0211         int time = 1;
0212 
0213         while (std::getline(iss, token, ':')) {
0214           switch (time) {
0215             case 1:
0216               property = token;
0217               break;
0218             case 2:
0219               value = token;
0220               break;
0221             default:
0222               value += token;
0223               break;
0224           }
0225           time++;
0226         }
0227         trim(property);
0228         trim(value);
0229         if (property.empty()) {
0230           continue;
0231         }
0232 
0233         if (property == "model name") {
0234           compressWhitespace(value);
0235         }
0236         info.emplace_back(property, value);
0237       }
0238       return true;
0239     }
0240 
0241     std::string CPU::getModelFromCPUFeatures() const {
0242       using namespace cpu_features;
0243 
0244       std::string model;
0245 #if defined(CPU_FEATURES_ARCH_X86)
0246       const auto info{GetX86Info()};
0247       model = info.brand_string;
0248 #elif defined(CPU_FEATURES_ARCH_ARM)
0249       const auto info{GetArmInfo()};
0250       model = fmt::format("ARM {} {} {}", info.implementer, info.architecture, info.variant);
0251 #elif defined(CPU_FEATURES_ARCH_AARCH64)
0252       const auto info{GetAarch64Info()};
0253       model = fmt::format("aarch64 {} {}", info.implementer, info.variant);
0254 #elif defined(CPU_FEATURES_ARCH_PPC)
0255       const auto strings{GetPPCPlatformStrings()};
0256       model = strings.machine;
0257 #elif defined(CPU_FEATURES_ARCH_RISCV)
0258       const auto info{GetRiscvInfo()};
0259       model = fmt::format("riscv64 {} {}", info.vendor, info.uarch);
0260 #endif
0261       return model;
0262     }
0263 
0264     std::vector<std::string> CPU::getModels(const CPUInfoType &info) const {
0265       std::set<std::string> modelSet;
0266       for (const auto &entry : info) {
0267         if (entry.first == "model name") {
0268           modelSet.insert(entry.second);
0269         }
0270       }
0271       std::vector<std::string> modelsVector(modelSet.begin(), modelSet.end());
0272       // If "model name" isn't present in /proc/cpuinfo, see what we can get
0273       // from cpu_features
0274       if (modelsVector.empty()) {
0275         modelsVector.emplace_back(getModelFromCPUFeatures());
0276       }
0277       return modelsVector;
0278     }
0279 
0280     std::string CPU::formatModels(const std::vector<std::string> &models) const {
0281       std::stringstream ss;
0282       int model = 0;
0283       for (const auto &modelname : models) {
0284         if (model++ != 0) {
0285           ss << ", ";
0286         }
0287         ss << modelname;
0288       }
0289       return ss.str();
0290     }
0291 
0292     double CPU::getAverageSpeed(const CPUInfoType &info) const {
0293       double averageCoreSpeed = 0.0;
0294       unsigned coreCount = 0;
0295       for (const auto &entry : info) {
0296         if (entry.first == "cpu MHz") {
0297           try {
0298             averageCoreSpeed += std::stod(entry.second);
0299           } catch (const std::logic_error &e) {
0300             LogWarning("CPU::getAverageSpeed") << "stod(" << entry.second << ") conversion error: " << e.what();
0301           }
0302           coreCount++;
0303         }
0304       }
0305       if (!coreCount) {
0306         return 0;
0307       }
0308       return averageCoreSpeed / static_cast<double>(coreCount);
0309     }
0310   }  // namespace service
0311 }  // namespace edm
0312 
0313 #include "FWCore/ServiceRegistry/interface/ServiceMaker.h"
0314 
0315 using edm::service::CPU;
0316 using CPUMaker = edm::serviceregistry::AllArgsMaker<edm::CPUServiceBase, CPU>;
0317 DEFINE_FWK_SERVICE_MAKER(CPU, CPUMaker);