File indexing completed on 2023-03-17 11:03:40
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 #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 }
0073 }
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
0094
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 }
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()) {
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
0269
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 }
0307 }
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);