Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2022-06-11 03:13:10

0001 // -*- C++ -*-
0002 //
0003 // Package:     Services
0004 // Class  :     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/ServiceRegistry/interface/ActivityRegistry.h"
0013 #include "FWCore/ServiceRegistry/interface/Service.h"
0014 #include "FWCore/ParameterSet/interface/ParameterSet.h"
0015 #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
0016 #include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
0017 #include "FWCore/Utilities/interface/CPUServiceBase.h"
0018 
0019 #include <iostream>
0020 #include <sys/time.h>
0021 #include <sys/resource.h>
0022 #include <cstdio>
0023 #include <string>
0024 #include <fstream>
0025 #include <sstream>
0026 #include <set>
0027 
0028 #ifdef __linux__
0029 #include <sched.h>
0030 #include <cerrno>
0031 #endif
0032 
0033 namespace edm {
0034 
0035   namespace service {
0036     class CPU : public CPUServiceBase {
0037     public:
0038       CPU(ParameterSet const &, ActivityRegistry &);
0039       ~CPU() override;
0040 
0041       static void fillDescriptions(ConfigurationDescriptions &descriptions);
0042 
0043       bool cpuInfo(std::string &models, double &avgSpeed) override;
0044 
0045     private:
0046       const bool reportCPUProperties_;
0047 
0048       bool cpuInfoImpl(std::string &models, double &avgSpeed, Service<JobReport> *reportSvc);
0049       bool parseCPUInfo(std::vector<std::pair<std::string, std::string>> &info);
0050       std::string getModels(const std::vector<std::pair<std::string, std::string>> &info);
0051       double getAverageSpeed(const std::vector<std::pair<std::string, std::string>> &info);
0052       void postEndJob();
0053     };
0054 
0055     inline bool isProcessWideService(CPU const *) { return true; }
0056   }  // namespace service
0057 }  // namespace edm
0058 
0059 namespace edm {
0060   namespace service {
0061     namespace {
0062 
0063       std::string i2str(int i) {
0064         std::ostringstream t;
0065         t << i;
0066         return t.str();
0067       }
0068 
0069       std::string d2str(double d) {
0070         std::ostringstream t;
0071         t << d;
0072         return t.str();
0073       }
0074 
0075       double str2d(std::string s) { return atof(s.c_str()); }
0076 
0077       void trim(std::string &s, const std::string &drop = " \t") {
0078         std::string::size_type p = s.find_last_not_of(drop);
0079         if (p != std::string::npos) {
0080           s = s.erase(p + 1);
0081         }
0082         s = s.erase(0, s.find_first_not_of(drop));
0083       }
0084 
0085       std::string eraseExtraSpaces(std::string s) {
0086         bool founded = false;
0087         std::string aux;
0088         for (std::string::const_iterator iter = s.begin(); iter != s.end(); iter++) {
0089           if (founded) {
0090             if (*iter == ' ')
0091               founded = true;
0092             else {
0093               aux += " ";
0094               aux += *iter;
0095               founded = false;
0096             }
0097           } else {
0098             if (*iter == ' ')
0099               founded = true;
0100             else
0101               aux += *iter;
0102           }
0103         }
0104         return aux;
0105       }
0106 
0107       // Determine the CPU set size; if this can be successfully determined, then this
0108       // returns true.
0109       bool getCpuSetSize(unsigned &set_size) {
0110 #ifdef __linux__
0111         cpu_set_t *cpusetp;
0112         unsigned current_size = 128;
0113         unsigned cpu_count = 0;
0114         while (current_size * 2 > current_size) {
0115           cpusetp = CPU_ALLOC(current_size);
0116           CPU_ZERO_S(CPU_ALLOC_SIZE(current_size), cpusetp);
0117 
0118           if (sched_getaffinity(0, CPU_ALLOC_SIZE(current_size), cpusetp)) {
0119             CPU_FREE(cpusetp);
0120             if (errno == EINVAL) {
0121               current_size *= 2;
0122               continue;
0123             }
0124             return false;
0125           }
0126           cpu_count = CPU_COUNT_S(CPU_ALLOC_SIZE(current_size), cpusetp);
0127           CPU_FREE(cpusetp);
0128           break;
0129         }
0130         set_size = cpu_count;
0131         return true;
0132 #else
0133         return false;
0134 #endif
0135       }
0136     }  // namespace
0137 
0138     CPU::CPU(const ParameterSet &iPS, ActivityRegistry &iRegistry)
0139         : reportCPUProperties_(iPS.getUntrackedParameter<bool>("reportCPUProperties")) {
0140       iRegistry.watchPostEndJob(this, &CPU::postEndJob);
0141     }
0142 
0143     CPU::~CPU() {}
0144 
0145     void CPU::fillDescriptions(edm::ConfigurationDescriptions &descriptions) {
0146       edm::ParameterSetDescription desc;
0147       desc.addUntracked<bool>("reportCPUProperties", false);
0148       descriptions.add("CPU", desc);
0149     }
0150 
0151     void CPU::postEndJob() {
0152       Service<JobReport> reportSvc;
0153 
0154       std::vector<std::pair<std::string, std::string>> info;
0155       if (!parseCPUInfo(info)) {
0156         return;
0157       }
0158 
0159       std::string models = getModels(info);
0160       double avgSpeed = getAverageSpeed(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", i2str(totalNumberCPUs)}, {"averageCoreSpeed", d2str(avgSpeed)}, {"CPUModels", models}};
0187       unsigned set_size = -1;
0188       if (getCpuSetSize(set_size)) {
0189         reportCPUProperties.insert(std::make_pair("cpusetCount", i2str(set_size)));
0190       }
0191       reportSvc->reportPerformanceSummary("SystemCPU", reportCPUProperties);
0192     }
0193 
0194     bool CPU::cpuInfo(std::string &models, double &avgSpeed) {
0195       std::vector<std::pair<std::string, std::string>> info;
0196       if (!parseCPUInfo(info)) {
0197         return false;
0198       }
0199 
0200       models = getModels(info);
0201       avgSpeed = getAverageSpeed(info);
0202       return true;
0203     }
0204 
0205     bool CPU::parseCPUInfo(std::vector<std::pair<std::string, std::string>> &info) {
0206       info.clear();
0207       std::ifstream fcpuinfo("/proc/cpuinfo");
0208       if (!fcpuinfo.is_open()) {
0209         return false;
0210       }
0211       while (!fcpuinfo.eof()) {
0212         std::string buf;
0213         std::getline(fcpuinfo, buf);
0214 
0215         std::istringstream iss(buf);
0216         std::string token;
0217         std::string property;
0218         std::string value;
0219 
0220         int time = 1;
0221 
0222         while (std::getline(iss, token, ':')) {
0223           switch (time) {
0224             case 1:
0225               property = token;
0226               break;
0227             case 2:
0228               value = token;
0229               break;
0230             default:
0231               value += token;
0232               break;
0233           }
0234           time++;
0235         }
0236         trim(property);
0237         trim(value);
0238         if (property.empty()) {
0239           continue;
0240         }
0241 
0242         if (property == "model name") {
0243           value = eraseExtraSpaces(value);
0244         }
0245         info.emplace_back(property, value);
0246       }
0247       return true;
0248     }
0249 
0250     std::string CPU::getModels(const std::vector<std::pair<std::string, std::string>> &info) {
0251       std::set<std::string> models;
0252       for (const auto &entry : info) {
0253         if (entry.first == "model name") {
0254           models.insert(entry.second);
0255         }
0256       }
0257       std::stringstream ss;
0258       int model = 0;
0259       for (const auto &modelname : models) {
0260         if (model++ != 0) {
0261           ss << ", ";
0262         }
0263         ss << modelname;
0264       }
0265       return ss.str();
0266     }
0267 
0268     double CPU::getAverageSpeed(const std::vector<std::pair<std::string, std::string>> &info) {
0269       double averageCoreSpeed = 0.0;
0270       unsigned coreCount = 0;
0271       for (const auto &entry : info) {
0272         if (entry.first == "cpu MHz") {
0273           averageCoreSpeed += str2d(entry.second);
0274           coreCount++;
0275         }
0276       }
0277       if (!coreCount) {
0278         return 0;
0279       }
0280       return averageCoreSpeed / static_cast<double>(coreCount);
0281     }
0282   }  // namespace service
0283 }  // namespace edm
0284 
0285 #include "FWCore/ServiceRegistry/interface/ServiceMaker.h"
0286 
0287 using edm::service::CPU;
0288 typedef edm::serviceregistry::AllArgsMaker<edm::CPUServiceBase, CPU> CPUMaker;
0289 DEFINE_FWK_SERVICE_MAKER(CPU, CPUMaker);