Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-07-31 22:58: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/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   }  // namespace service
0075 }  // namespace edm
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       // Determine the CPU set size; if this can be successfully determined, then this
0096       // returns true.
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     }  // namespace
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()) {  // first core
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       // If "model name" isn't present in /proc/cpuinfo, see what we can get
0274       // from cpu_features
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   }  // namespace service
0312 }  // namespace edm
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);