Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:13:04

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