CMS 3D CMS Logo

CPU.cc
Go to the documentation of this file.
1 // -*- C++ -*-
2 //
3 // Package: Services
4 // Class : edm::service::CPU
5 //
6 // Implementation:
7 //
8 // Original Author: Natalia Garcia
9 // CPU.cc: v 1.0 2009/01/08 11:31:07
10 
20 
21 #include "cpu_features/cpu_features_macros.h"
22 
23 #if defined(CPU_FEATURES_ARCH_X86)
24 #include "cpu_features/cpuinfo_x86.h"
25 #elif defined(CPU_FEATURES_ARCH_ARM)
26 #include "cpu_features/cpuinfo_arm.h"
27 #elif defined(CPU_FEATURES_ARCH_AARCH64)
28 #include "cpu_features/cpuinfo_aarch64.h"
29 #elif defined(CPU_FEATURES_ARCH_PPC)
30 #include "cpu_features/cpuinfo_ppc.h"
31 #elif defined(CPU_FEATURES_ARCH_RISCV)
32 #include "cpu_features/cpuinfo_riscv.h"
33 #endif
34 
35 #include <cstdlib>
36 #include <string>
37 #include <fstream>
38 #include <sstream>
39 #include <map>
40 #include <set>
41 #include <utility>
42 #include <vector>
43 #include <fmt/format.h>
44 
45 #ifdef __linux__
46 #include <sched.h>
47 #include <cerrno>
48 #endif
49 
50 namespace edm {
51  using CPUInfoType = std::vector<std::pair<std::string, std::string>>;
52 
53  namespace service {
54  class CPU : public CPUServiceBase {
55  public:
56  CPU(ParameterSet const &, ActivityRegistry &);
57  ~CPU() override = default;
58 
59  static void fillDescriptions(ConfigurationDescriptions &descriptions);
60 
61  private:
64 
65  bool parseCPUInfo(CPUInfoType &info) const;
66  std::vector<std::string> getModels(const CPUInfoType &info) const;
67  std::string formatModels(const std::vector<std::string> &models) const;
69  double getAverageSpeed(const CPUInfoType &info) const;
70  void postEndJob();
71  };
72 
73  inline bool isProcessWideService(CPU const *) { return true; }
74  } // namespace service
75 } // namespace edm
76 
77 namespace edm {
78  namespace service {
79  namespace {
80 
81  void trim(std::string &s, const std::string &drop = " \t") {
82  std::string::size_type p = s.find_last_not_of(drop);
83  if (p != std::string::npos) {
84  s = s.erase(p + 1);
85  }
86  s = s.erase(0, s.find_first_not_of(drop));
87  }
88 
89  void compressWhitespace(std::string &s) {
90  auto last =
91  std::unique(s.begin(), s.end(), [](const auto a, const auto b) { return std::isspace(a) && a == b; });
92  s.erase(last, s.end());
93  }
94 
95  // Determine the CPU set size; if this can be successfully determined, then this
96  // returns true.
97  bool getCpuSetSize(unsigned &set_size) {
98 #ifdef __linux__
99  cpu_set_t *cpusetp;
100  unsigned current_size = 128;
101  unsigned cpu_count = 0;
102  while (current_size * 2 > current_size) {
103  cpusetp = CPU_ALLOC(current_size);
104  CPU_ZERO_S(CPU_ALLOC_SIZE(current_size), cpusetp);
105 
106  if (sched_getaffinity(0, CPU_ALLOC_SIZE(current_size), cpusetp)) {
107  CPU_FREE(cpusetp);
108  if (errno == EINVAL) {
109  current_size *= 2;
110  continue;
111  }
112  return false;
113  }
114  cpu_count = CPU_COUNT_S(CPU_ALLOC_SIZE(current_size), cpusetp);
115  CPU_FREE(cpusetp);
116  break;
117  }
118  set_size = cpu_count;
119  return true;
120 #else
121  return false;
122 #endif
123  }
124  } // namespace
125 
126  CPU::CPU(const ParameterSet &iPS, ActivityRegistry &iRegistry)
127  : reportCPUProperties_(iPS.getUntrackedParameter<bool>("reportCPUProperties")),
128  disableJobReportOutput_(iPS.getUntrackedParameter<bool>("disableJobReportOutput")) {
129  edm::Service<edm::ResourceInformation> resourceInformationService;
130  if (resourceInformationService.isAvailable()) {
132  if (parseCPUInfo(info)) {
133  const auto models{getModels(info)};
134  resourceInformationService->setCPUModels(models);
135  resourceInformationService->setCpuModelsFormatted(formatModels(models));
136  resourceInformationService->setCpuAverageSpeed(getAverageSpeed(info));
137  }
138  }
139  iRegistry.watchPostEndJob(this, &CPU::postEndJob);
140  }
141 
144  desc.addUntracked<bool>("reportCPUProperties", false);
145  desc.addUntracked<bool>("disableJobReportOutput", false);
146  descriptions.add("CPU", desc);
147  }
148 
151  return;
152  }
153 
154  Service<JobReport> reportSvc;
155 
157  if (!parseCPUInfo(info)) {
158  return;
159  }
160 
161  const auto models{formatModels(getModels(info))};
162  unsigned totalNumberCPUs = 0;
163  std::map<std::string, std::string> currentCoreProperties;
164  std::string currentCore;
165 
166  for (const auto &entry : info) {
167  if (entry.first == "processor") {
168  if (reportCPUProperties_) {
169  if (currentCore.empty()) { // first core
170  currentCore = entry.second;
171  } else {
172  reportSvc->reportPerformanceForModule("SystemCPU", "CPU-" + currentCore, currentCoreProperties);
173  currentCoreProperties.clear();
174  currentCore = entry.second;
175  }
176  }
177  totalNumberCPUs++;
178  } else if (reportCPUProperties_) {
179  currentCoreProperties.insert(entry);
180  }
181  }
182  if (!currentCore.empty() && reportCPUProperties_) {
183  reportSvc->reportPerformanceForModule("SystemCPU", "CPU-" + currentCore, currentCoreProperties);
184  }
185 
186  std::map<std::string, std::string> reportCPUProperties{
187  {"totalCPUs", std::to_string(totalNumberCPUs)},
188  {"averageCoreSpeed", std::to_string(getAverageSpeed(info))},
189  {"CPUModels", models}};
190  unsigned set_size = -1;
191  if (getCpuSetSize(set_size)) {
192  reportCPUProperties.insert(std::make_pair("cpusetCount", std::to_string(set_size)));
193  }
194  reportSvc->reportPerformanceSummary("SystemCPU", reportCPUProperties);
195  }
196 
198  info.clear();
199  std::ifstream fcpuinfo("/proc/cpuinfo");
200  if (!fcpuinfo.is_open()) {
201  return false;
202  }
203  while (!fcpuinfo.eof()) {
205  std::getline(fcpuinfo, buf);
206 
207  std::istringstream iss(buf);
209  std::string property;
211 
212  int time = 1;
213 
214  while (std::getline(iss, token, ':')) {
215  switch (time) {
216  case 1:
217  property = token;
218  break;
219  case 2:
220  value = token;
221  break;
222  default:
223  value += token;
224  break;
225  }
226  time++;
227  }
228  trim(property);
229  trim(value);
230  if (property.empty()) {
231  continue;
232  }
233 
234  if (property == "model name") {
235  compressWhitespace(value);
236  }
237  info.emplace_back(property, value);
238  }
239  return true;
240  }
241 
243  using namespace cpu_features;
244 
246 #if defined(CPU_FEATURES_ARCH_X86)
247  const auto info{GetX86Info()};
248  model = info.brand_string;
249 #elif defined(CPU_FEATURES_ARCH_ARM)
250  const auto info{GetArmInfo()};
251  model = fmt::format("ARM {} {} {}", info.implementer, info.architecture, info.variant);
252 #elif defined(CPU_FEATURES_ARCH_AARCH64)
253  const auto info{GetAarch64Info()};
254  model = fmt::format("aarch64 {} {}", info.implementer, info.variant);
255 #elif defined(CPU_FEATURES_ARCH_PPC)
256  const auto strings{GetPPCPlatformStrings()};
257  model = strings.machine;
258 #elif defined(CPU_FEATURES_ARCH_RISCV)
259  const auto info{GetRiscvInfo()};
260  model = fmt::format("riscv64 {} {}", info.vendor, info.uarch);
261 #endif
262  return model;
263  }
264 
265  std::vector<std::string> CPU::getModels(const CPUInfoType &info) const {
266  std::set<std::string> modelSet;
267  for (const auto &entry : info) {
268  if (entry.first == "model name") {
269  modelSet.insert(entry.second);
270  }
271  }
272  std::vector<std::string> modelsVector(modelSet.begin(), modelSet.end());
273  // If "model name" isn't present in /proc/cpuinfo, see what we can get
274  // from cpu_features
275  if (modelsVector.empty()) {
276  modelsVector.emplace_back(getModelFromCPUFeatures());
277  }
278  return modelsVector;
279  }
280 
281  std::string CPU::formatModels(const std::vector<std::string> &models) const {
282  std::stringstream ss;
283  int model = 0;
284  for (const auto &modelname : models) {
285  if (model++ != 0) {
286  ss << ", ";
287  }
288  ss << modelname;
289  }
290  return ss.str();
291  }
292 
293  double CPU::getAverageSpeed(const CPUInfoType &info) const {
294  double averageCoreSpeed = 0.0;
295  unsigned coreCount = 0;
296  for (const auto &entry : info) {
297  if (entry.first == "cpu MHz") {
298  try {
299  averageCoreSpeed += std::stod(entry.second);
300  } catch (const std::logic_error &e) {
301  LogWarning("CPU::getAverageSpeed") << "stod(" << entry.second << ") conversion error: " << e.what();
302  }
303  coreCount++;
304  }
305  }
306  if (!coreCount) {
307  return 0;
308  }
309  return averageCoreSpeed / static_cast<double>(coreCount);
310  }
311  } // namespace service
312 } // namespace edm
313 
315 
316 using edm::service::CPU;
std::vector< std::pair< std::string, std::string > > CPUInfoType
Definition: CPU.cc:51
static const TGPicture * info(bool iBackgroundIsBlack)
#define DEFINE_FWK_SERVICE_MAKER(concrete, maker)
Definition: ServiceMaker.h:102
~CPU() override=default
void postEndJob()
Definition: CPU.cc:149
void watchPostEndJob(PostEndJob::slot_type const &iSlot)
bool isProcessWideService(TFileService const *)
Definition: TFileService.h:98
static void trim(std::string &s)
Definition: models.py:1
uint16_t size_type
std::string formatModels(const std::vector< std::string > &models) const
Definition: CPU.cc:281
virtual void setCPUModels(std::vector< std::string > const &)=0
static std::string to_string(const XMLCh *ch)
std::vector< std::string > getModels(const CPUInfoType &info) const
Definition: CPU.cc:265
def unique(seq, keepstr=True)
Definition: tier0.py:24
Definition: value.py:1
CPU(ParameterSet const &, ActivityRegistry &)
Definition: CPU.cc:126
double b
Definition: hdecay.h:120
void add(std::string const &label, ParameterSetDescription const &psetDescription)
static void fillDescriptions(ConfigurationDescriptions &descriptions)
Definition: CPU.cc:142
HLT enums.
double a
Definition: hdecay.h:121
virtual void setCpuModelsFormatted(std::string const &)=0
virtual void setCpuAverageSpeed(double)=0
const bool reportCPUProperties_
Definition: CPU.cc:62
bool isAvailable() const
Definition: Service.h:40
bool parseCPUInfo(CPUInfoType &info) const
Definition: CPU.cc:197
Log< level::Warning, false > LogWarning
const bool disableJobReportOutput_
Definition: CPU.cc:63
double getAverageSpeed(const CPUInfoType &info) const
Definition: CPU.cc:293
std::string getModelFromCPUFeatures() const
Definition: CPU.cc:242