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