Skip to content

Commit 3121f37

Browse files
committed
Added ModuleEventAllocMonitor Service
- refactored code used from ModuleAllocMonitor
1 parent 06a67de commit 3121f37

File tree

8 files changed

+1052
-105
lines changed

8 files changed

+1052
-105
lines changed

PerfTools/AllocMonitor/README.md

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ This service is multi-thread safe. Note that when run multi-threaded the maximum
8585
This service registers a monitor when the service is created (after python parsing is finished but before any modules
8686
have been loaded into cmsRun) and prints its accumulated information to the specified file at specified intervals. Both
8787
the file name and interval are specified by setting parameters of the service in the configuration. The parameters are
88-
- filename: name of file to which to write reports
89-
- millisecondsPerMeasurement: number of milliseconds to wait between making each report
88+
- `filename`: name of file to which to write reports
89+
- `millisecondsPerMeasurement`: number of milliseconds to wait between making each report
9090

9191
The output file contains the following information on each line
9292
- The time, in milliseconds, since the service was created
@@ -108,10 +108,27 @@ This service registers a monitor when the service is created (after python parsi
108108
have been loaded into cmsRun) and writes module related information to the specified file. The file name, an optional
109109
list of module names, and an optional number of initial events to skip are specified by setting parameters of the
110110
service in the configuration. The parameters are
111-
- filename: name of file to which to write reports
112-
- moduleNames: list of modules which should have their information added to the file. An empty list specifies all modules should be included.
113-
- nEventsToSkip: the number of initial events that must be processed before reporting happens.
111+
- `filename`: name of file to which to write reports
112+
- `moduleNames`: list of modules which should have their information added to the file. An empty list specifies all modules should be included.
113+
- `nEventsToSkip`: the number of initial events that must be processed before reporting happens.
114114

115115
The beginning of the file contains a description of the structure and contents of the file.
116116

117-
This service is multi-thread safe.
117+
This service is multi-thread safe.
118+
119+
120+
### ModuleEventAllocMonitor
121+
This service registers a monitor when the service is created (after python parsing is finished but before any modules
122+
have been loaded into cmsRun) and writes event based module related information to the specified file. The service
123+
keeps track of the address of each allocation requested during an event from each module and pairs them with any
124+
deallocation using the same address. The list of addresses are kept until the event data products are deleted at which
125+
time the dallocations are paired with allocations done in a module. The list of addresses are then cleared (to keep memory usage down) but the amount of unassociated deallocations for each module is recorded per event.
126+
The file name, an optional list of module names, and an optional number of initial events to skip are specified by setting parameters of the
127+
service in the configuration. The parameters are
128+
- `filename`: name of file to which to write reports
129+
- `moduleNames`: list of modules which should have their information added to the file. An empty list specifies all modules should be included.
130+
- `nEventsToSkip`: the number of initial events that must be processed before reporting happens.
131+
132+
The beginning of the file contains a description of the structure and contents of the file. The file can be analyzed with the helper script `edmModuleEventAllocMonitorAnalyze.py`. The script can be used to find modules where the memory is being retained between Events as well as modules where the memory appears to be growing Event to Event. Use `--help` with the script for a full description.
133+
134+
This service is multi-thread safe.

PerfTools/AllocMonitor/plugins/ModuleAllocMonitor.cc

Lines changed: 3 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -32,106 +32,10 @@
3232

3333
#include "moduleAlloc_setupFile.h"
3434
#include "ThreadAllocInfo.h"
35+
#include "ThreadTracker.h"
3536

3637
namespace {
37-
inline auto thread_id() {
38-
#if defined(ALLOC_USE_PTHREADS)
39-
/*NOTE: if use pthread_self, the values returned by linux had
40-
lots of hash collisions when using a simple % hash. Worked
41-
better if first divided value by 0x700 and then did %.
42-
[test done on el8]
43-
*/
44-
return pthread_self();
45-
#else
46-
return syscall(SYS_gettid);
47-
#endif
48-
}
49-
50-
struct ThreadTracker {
51-
static constexpr unsigned int kHashedEntries = 128;
52-
static constexpr unsigned int kExtraEntries = 128;
53-
static constexpr unsigned int kTotalEntries = kHashedEntries + kExtraEntries;
54-
using entry_type = decltype(thread_id());
55-
static constexpr entry_type kUnusedEntry = ~entry_type(0);
56-
std::array<std::atomic<entry_type>, kHashedEntries> hashed_threads_;
57-
std::array<std::atomic<entry_type>, kExtraEntries> extra_threads_;
58-
59-
ThreadTracker() {
60-
//put a value which will not match the % used when looking up the entry
61-
entry_type entry = 0;
62-
for (auto& v : hashed_threads_) {
63-
v = ++entry;
64-
}
65-
//assume kUsedEntry is not a valid thread-id
66-
for (auto& v : extra_threads_) {
67-
v = kUnusedEntry;
68-
}
69-
}
70-
71-
std::size_t thread_index() {
72-
auto id = thread_id();
73-
auto index = thread_index_guess(id);
74-
auto used_id = hashed_threads_[index].load();
75-
76-
if (id == used_id) {
77-
return index;
78-
}
79-
//try to be first thread to grab the index
80-
auto expected = entry_type(index + 1);
81-
if (used_id == expected) {
82-
if (hashed_threads_[index].compare_exchange_strong(expected, id)) {
83-
return index;
84-
} else {
85-
//another thread just beat us so have to go to non-hash storage
86-
return find_new_index(id);
87-
}
88-
}
89-
//search in non-hash storage
90-
return find_index(id);
91-
}
92-
93-
private:
94-
std::size_t thread_index_guess(entry_type id) const {
95-
#if defined(ALLOC_USE_PTHREADS)
96-
return (id / 0x700) % kHashedEntries;
97-
#else
98-
return id % kHashedEntries;
99-
#endif
100-
}
101-
102-
std::size_t find_new_index(entry_type id) {
103-
std::size_t index = 0;
104-
for (auto& v : extra_threads_) {
105-
entry_type expected = kUnusedEntry;
106-
if (v == expected) {
107-
if (v.compare_exchange_strong(expected, id)) {
108-
return index + kHashedEntries;
109-
}
110-
}
111-
++index;
112-
}
113-
//failed to find an open entry
114-
abort();
115-
return 0;
116-
}
117-
118-
std::size_t find_index(entry_type id) {
119-
std::size_t index = 0;
120-
for (auto const& v : extra_threads_) {
121-
if (v == id) {
122-
return index + kHashedEntries;
123-
}
124-
++index;
125-
}
126-
return find_new_index(id);
127-
}
128-
};
129-
130-
static ThreadTracker& getTracker() {
131-
static ThreadTracker s_tracker;
132-
return s_tracker;
133-
}
134-
38+
using namespace cms::perftools::allocMon;
13539
using namespace edm::service::moduleAlloc;
13640
class MonitorAdaptor : public cms::perftools::AllocMonitorBase {
13741
public:
@@ -149,7 +53,7 @@ namespace {
14953
private:
15054
static ThreadAllocInfo& threadAllocInfo() {
15155
static ThreadAllocInfo s_info[ThreadTracker::kTotalEntries];
152-
return s_info[getTracker().thread_index()];
56+
return s_info[ThreadTracker::instance().thread_index()];
15357
}
15458
void allocCalled(size_t iRequested, size_t iActual, void const*) final {
15559
auto& allocInfo = threadAllocInfo();

0 commit comments

Comments
 (0)