ATLAS Offline Software
Loading...
Searching...
No Matches
PerfMonMTUtils.h
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
5/*
6 * @authors: Alaettin Serhan Mete, Hasan Ozturk - alaettin.serhan.mete@cern.ch, haozturk@cern.ch
7 */
8
9#ifndef PERFMONCOMPS_PERFMONMTUTILS_H
10#define PERFMONCOMPS_PERFMONMTUTILS_H
11
12// Thread-safety-checker
14
15// PerfMon includes
16
17// STL includes
18#include <dlfcn.h> // for dlsym
19#include <fcntl.h> // for open function
20#include <malloc.h> // for mallinfo function
21#include <sys/stat.h> // to check whether /proc/* exists in the machine
22
23#include <chrono>
24#include <cstdint>
25#include <ctime>
26#include <fstream>
27#include <map>
28#include <string>
29
30typedef std::map<std::string, int64_t> MemoryMap_t; // Component : Memory Measurement(kB)
31
32/*
33 * Inline function prototypes
34 */
35inline MemoryMap_t operator-(const MemoryMap_t& map1, const MemoryMap_t& map2);
36
37/*
38 * Necessary tools
39 */
40namespace PMonMT {
41
42 // Base methods for collecting data
43 double get_thread_cpu_time();
44 double get_process_cpu_time();
45 double get_wall_time();
46
47 // Non-efficient memory measurement
49
50 // Efficient memory measurements
51 double get_vmem();
52
53 // Simple check if directory exists
54 bool doesDirectoryExist(const std::string& dir);
55
56 // Malloc memory measurements
57 double get_malloc_kb ATLAS_NOT_THREAD_SAFE ();
58
59 // Get library name from symbol
60 const char * symb2lib(const char* symbol, const char* failstr);
61
62 // Step name and Component name pairs. Ex: Initialize - StoreGateSvc
63 struct StepComp {
64 std::string stepName;
65 std::string compName;
66
67 // Overload < operator, because we are using a custom key(StepComp) for std::map
68 bool operator<(const StepComp& sc) const {
69 return std::make_pair(this->stepName, this->compName) < std::make_pair(sc.stepName, sc.compName);
70 }
71 };
72
73 // ComponentMeasurement
75
76 // Variables to store measurements
77 double cpu_time{}, wall_time{}; // Timing
78 double vmem{}, malloc{}; // Memory: Vmem, Malloc
79
80 // Capture component-level measurements
81 void capture() {
82
83 // Timing
86 }
87
88 // Capture component-level memory measurements
89 bool capture_memory ATLAS_NOT_THREAD_SAFE() {
90
91 // Memory
92 malloc = get_malloc_kb();
93 vmem = get_vmem();
94 return true; // dummy return value for use with thread-checker macros
95 }
96
97 // Constructor
99
100 }; // End ComponentMeasurement
101
102 // Component Data
104
105 // These variables are used to calculate and store the component level measurements
106 uint64_t m_call_count{};
111
112 // [Component Level Monitoring] : Start
113 void addPointStart(const ComponentMeasurement& meas, const bool doMem = false) {
114
115 // Timing
116 m_tmp_cpu = meas.cpu_time;
117 m_tmp_wall = meas.wall_time;
118
119 // Memory if only necessary
120 if (!doMem) return;
121
122 // Memory
123 m_tmp_malloc = meas.malloc;
124 m_tmp_vmem = meas.vmem;
125
126 }
127
128 // [Component Level Monitoring] : Stop
129 void addPointStop(const ComponentMeasurement& meas, const bool doMem = false) {
130
131 // Call count
132 m_call_count++;
133
134 // Timing
137
138 // Memory if only necessary
139 if (!doMem) return;
140
141 // Memory
143 m_delta_vmem += meas.vmem - m_tmp_vmem;
144
145 }
146
147 // Convenience methods
148 uint64_t getCallCount() const { return m_call_count; }
149 void add2CallCount(uint64_t val) { m_call_count += val; }
150
151 double getDeltaCPU() const { return m_delta_cpu; }
152 void add2DeltaCPU(double val) { m_delta_cpu += val; }
153
154 double getDeltaWall() const { return m_delta_wall; }
155 void add2DeltaWall(double val) { m_delta_wall += val; }
156
157 double getDeltaVmem() const { return m_delta_vmem; }
158 void add2DeltaVmem(double val) { m_delta_vmem += val; }
159
160 double getDeltaMalloc() const { return m_delta_malloc; }
161 void add2DeltaMalloc(double val) { m_delta_malloc += val; }
162
163 // Constructor
166
167 }; // End ComponentData
168
169 // Snapshot Measurement
171
172 // Variables to store measurements
173 double cpu_time{}, wall_time{}; // Timing
174 MemoryMap_t mem_stats; // Memory: Vmem, Rss, Pss, Swap
175
176 // Capture snapshot measurements
177 void capture() {
178
179 // Timing
182
183 // Memory
185
186 }
187
188 // Constructor
190 mem_stats["vmem"] = 0; mem_stats["pss"] = 0; mem_stats["rss"] = 0; mem_stats["swap"] = 0;
191 }
192
193 }; // End SnapshotMeasurement
194
195 // Event Level Data
197
198 // This map is used to store the event level measurements
199 typedef std::map<uint64_t, SnapshotMeasurement> EventMeasMap_t; // Event number: Measurement
201
202 // [Event Level Monitoring] : Record the measurement for the current checkpoint
203 void recordEvent(const SnapshotMeasurement& meas, const int eventCount) {
204
205 // Timing
206 m_eventLevelDeltaMap[eventCount].cpu_time = meas.cpu_time;
207 m_eventLevelDeltaMap[eventCount].wall_time = meas.wall_time - m_offset_wall;
208
209 // Memory
210 m_eventLevelDeltaMap[eventCount].mem_stats = meas.mem_stats;
211
212 }
213
214 // Wall time offset for event level monitoring
216
217 // Convenience methods
218 void set_wall_time_offset(const double wall_time_offset) { m_offset_wall = wall_time_offset; }
219
222 }
223
224 uint64_t getNMeasurements() const {
225 return m_eventLevelDeltaMap.size();
226 }
227
228 double getEventLevelCpuTime(const uint64_t event_count) const {
229 return m_eventLevelDeltaMap.at(event_count).cpu_time;
230 }
231
232 double getEventLevelWallTime(const uint64_t event_count) const {
233 return m_eventLevelDeltaMap.at(event_count).wall_time;
234 }
235
236 int64_t getEventLevelMemory(const uint64_t event_count,
237 const std::string& stat) const {
238 return m_eventLevelDeltaMap.at(event_count).mem_stats.at(stat);
239 }
240
241 int64_t getEventLevelMemoryMax(const std::string& stat) const {
242 int64_t result = 0;
243 for (const auto& it : getEventLevelData()) {
244 if (it.second.mem_stats.at(stat) > result) {
245 result = it.second.mem_stats.at(stat);
246 }
247 }
248 return result;
249 }
250
251 }; // Add EventLevelData
252
253 // Snapshot Data
255
256 // These variables are used to calculate and store the serial component level measurements
260
261 // [Snapshot Level Monitoring] : Start
263
264 // Timing
265 m_tmp_cpu = meas.cpu_time;
266 m_tmp_wall = meas.wall_time;
267
268 // Non-efficient memory measurements
270
271 }
272
273 // [Snapshot Level Monitoring] : Stop
275
276 // Timing
279
280 // Non-efficient memory measurements
282
283 }
284
285 // Convenience methods
286 double getDeltaCPU() const { return m_delta_cpu; }
287 void add2DeltaCPU(double val) { m_delta_cpu += val; }
288
289 double getDeltaWall() const { return m_delta_wall; }
290 void add2DeltaWall(double val) { m_delta_wall += val; }
291
292 int64_t getMemMonDeltaMap(const std::string& mem_stat) const {
293 return m_memMonDeltaMap.at(mem_stat);
294 }
295
296 // Constructor
298 m_memMonTmpMap["vmem"] = 0; m_memMonTmpMap["pss"] = 0; m_memMonTmpMap["rss"] = 0; m_memMonTmpMap["swap"] = 0;
299 m_memMonDeltaMap["vmem"] = 0; m_memMonDeltaMap["pss"] = 0; m_memMonDeltaMap["rss"] = 0; m_memMonDeltaMap["swap"] = 0;
300 }
301
302 }; // End SnapshotData
303
304} // namespace PMonMT
305
307// Inline methods:
309
310/*
311 * Thread specific CPU time measurement in ms
312 */
314 // Get the thread specific CPU time
315 struct timespec ctime;
316 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ctime);
317
318 // Return the measurement in ms
319 return static_cast<double>(ctime.tv_sec * 1.e3 + ctime.tv_nsec * 1.e-6);
320}
321
322/*
323 * Process specific CPU time measurement in ms
324 */
325inline double PMonMT::get_process_cpu_time() { return static_cast<double>(std::clock() * (1.e3 / CLOCKS_PER_SEC)); }
326
327/*
328 * Wall-time measurement since epoch in ms
329 */
330inline double PMonMT::get_wall_time() {
331 return static_cast<double>(std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1));
332}
333
334/*
335 * Memory statistics
336 */
337
338// Read from proc's smaps file. It is costly to do this operation too often.
339// In a realistic reconstruction job, the smaps for the the whole application can get large.
340// Therefore, this operation might take about 100 ms per call, which is fairly substantial.
341// However, this is one of the most reliable way to get PSS.
342// Therefore, keep it as is but don't call it too often!
344 // Result object
346
347 // Zero initialize
348 result["vmem"] = result["rss"] = result["pss"] = result["swap"] = 0;
349
350 // This is the input where we read the stats from
351 static const std::string fileName = "/proc/self/smaps";
352 std::ifstream smaps_file{fileName};
353
354 std::string line{}, key{}, value{};
355
356 // Loop over the file
357 while (smaps_file) {
358 // Read interesting key value pairs
359 smaps_file >> key >> value;
360 smaps_file.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
361
362 if(smaps_file) {
363 if (key == "Size:") {
364 result["vmem"] += std::stol(value);
365 }
366 if (key == "Rss:") {
367 result["rss"] += std::stol(value);
368 }
369 if (key == "Pss:") {
370 result["pss"] += std::stol(value);
371 }
372 if (key == "Swap:") {
373 result["swap"] += std::stol(value);
374 }
375 }
376 }
377
378 return result;
379}
380
381// This operation is less costly than the previous one. Since statm is a much smaller file compared to smaps.
382inline double PMonMT::get_vmem() {
383 // Result
384 double result = 0.;
385
386 // This is where we read the stats from
387 static const std::string fileName = "/proc/self/statm";
388 std::ifstream statm_file{fileName};
389
390 std::string vmem_in_pages{}, line{}; // vmem measured in pages
391
392 // We simply get the first line
393 if (getline(statm_file, line)) {
394 std::stringstream ss{line};
395 ss >> vmem_in_pages; // The first number in this file is the vmem measured in pages
396 }
397
398 static const double page_size = sysconf(_SC_PAGESIZE) / 1024.0; // page size in KB
399 result = std::stod(vmem_in_pages) * page_size;
400
401 return result;
402}
403
404inline MemoryMap_t operator-(const MemoryMap_t& map1, const MemoryMap_t& map2) {
405 MemoryMap_t result_map;
406 for (const auto& it : map1) {
407 result_map[it.first] = map1.at(it.first) - map2.at(it.first);
408 }
409 return result_map;
410}
411
412/*
413 * Simple check if a given directory exists
414 */
415inline bool PMonMT::doesDirectoryExist(const std::string& dir) {
416 struct stat buffer;
417 return (stat(dir.c_str(), &buffer) == 0);
418}
419
420/*
421 * Get Malloc (from SemiDetMisc.h)
422 */
423inline double PMonMT::get_malloc_kb ATLAS_NOT_THREAD_SAFE () {
424#ifndef __linux
425 return 0.0;
426#else
427 struct mallinfo2 m = mallinfo2();
428 return (m.uordblks+m.hblkhd)/1024.0;
429#endif
430}
431
432/*
433 * Get library name from symbol (from SemiDetMisc.h)
434 */
435inline const char * PMonMT::symb2lib(const char*symbol,const char * failstr = "unknown") {
436 void * addr = dlsym(RTLD_DEFAULT,symbol);
437 if (!addr) return failstr;
438 Dl_info di;
439 if (!dladdr(addr, &di)) return failstr;
440 if (!di.dli_fname) return failstr;
441 return di.dli_fname;
442}
443
444#endif // PERFMONCOMPS_PERFMONMTUTILS_H
static Double_t ss
static Double_t sc
MemoryMap_t operator-(const MemoryMap_t &map1, const MemoryMap_t &map2)
std::map< std::string, int64_t > MemoryMap_t
double PMonMT::get_malloc_kb ATLAS_NOT_THREAD_SAFE()
Install fatal handler with default options.
Define macros for attributes used to control the static checker.
double get_vmem()
double get_wall_time()
bool doesDirectoryExist(const std::string &dir)
double get_thread_cpu_time()
MemoryMap_t get_mem_stats()
const char * symb2lib(const char *symbol, const char *failstr)
double get_malloc_kb ATLAS_NOT_THREAD_SAFE()
double get_process_cpu_time()
void add2CallCount(uint64_t val)
void add2DeltaWall(double val)
double getDeltaVmem() const
double getDeltaCPU() const
uint64_t getCallCount() const
double getDeltaWall() const
void addPointStart(const ComponentMeasurement &meas, const bool doMem=false)
double getDeltaMalloc() const
void add2DeltaCPU(double val)
void add2DeltaVmem(double val)
void add2DeltaMalloc(double val)
void addPointStop(const ComponentMeasurement &meas, const bool doMem=false)
bool capture_memory ATLAS_NOT_THREAD_SAFE()
double getEventLevelCpuTime(const uint64_t event_count) const
void set_wall_time_offset(const double wall_time_offset)
const EventMeasMap_t & getEventLevelData() const
double getEventLevelWallTime(const uint64_t event_count) const
std::map< uint64_t, SnapshotMeasurement > EventMeasMap_t
EventMeasMap_t m_eventLevelDeltaMap
void recordEvent(const SnapshotMeasurement &meas, const int eventCount)
int64_t getEventLevelMemoryMax(const std::string &stat) const
int64_t getEventLevelMemory(const uint64_t event_count, const std::string &stat) const
uint64_t getNMeasurements() const
void addPointStop(const SnapshotMeasurement &meas)
double getDeltaCPU() const
void addPointStart(const SnapshotMeasurement &meas)
MemoryMap_t m_memMonDeltaMap
void add2DeltaCPU(double val)
void add2DeltaWall(double val)
int64_t getMemMonDeltaMap(const std::string &mem_stat) const
double getDeltaWall() const
std::string compName
bool operator<(const StepComp &sc) const
std::string stepName