CMS 3D CMS Logo

ModuleAllocMonitor.cc
Go to the documentation of this file.
1 // -*- C++ -*-
2 //
3 // Package: PerfTools/AllocMonitor
4 // Class : EventProcessingAllocMonitor
5 //
6 // Implementation:
7 // [Notes on implementation]
8 //
9 // Original Author: Christopher Jones
10 // Created: Mon, 21 Aug 2023 20:31:57 GMT
11 //
12 
13 // system include files
14 #include <atomic>
15 
16 // user include files
25 
26 #if defined(ALLOC_USE_PTHREADS)
27 #include <pthread.h>
28 #else
29 #include <unistd.h>
30 #include <sys/syscall.h>
31 #endif
32 
33 #include "moduleAlloc_setupFile.h"
34 #include "ThreadAllocInfo.h"
35 
36 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 
135  using namespace edm::service::moduleAlloc;
136  class MonitorAdaptor : public cms::perftools::AllocMonitorBase {
137  public:
138  static void startOnThread() { threadAllocInfo().reset(); }
139  static ThreadAllocInfo const& stopOnThread() {
140  auto& t = threadAllocInfo();
141  if (not t.active_) {
142  t.reset();
143  } else {
144  t.deactivate();
145  }
146  return t;
147  }
148 
149  private:
150  static ThreadAllocInfo& threadAllocInfo() {
151  static ThreadAllocInfo s_info[ThreadTracker::kTotalEntries];
152  return s_info[getTracker().thread_index()];
153  }
154  void allocCalled(size_t iRequested, size_t iActual, void const*) final {
155  auto& allocInfo = threadAllocInfo();
156  if (not allocInfo.active_) {
157  return;
158  }
159  allocInfo.nAllocations_ += 1;
160  allocInfo.requested_ += iRequested;
161 
162  if (allocInfo.maxSingleAlloc_ < iRequested) {
163  allocInfo.maxSingleAlloc_ = iRequested;
164  }
165 
166  allocInfo.presentActual_ += iActual;
167  if (allocInfo.presentActual_ > static_cast<long long>(allocInfo.maxActual_)) {
168  allocInfo.maxActual_ = allocInfo.presentActual_;
169  }
170  }
171  void deallocCalled(size_t iActual, void const*) final {
172  auto& allocInfo = threadAllocInfo();
173  if (not allocInfo.active_) {
174  return;
175  }
176 
177  allocInfo.nDeallocations_ += 1;
178  allocInfo.presentActual_ -= iActual;
179  if (allocInfo.presentActual_ < 0) {
180  if (allocInfo.minActual_ == 0 or allocInfo.minActual_ > allocInfo.presentActual_) {
181  allocInfo.minActual_ = allocInfo.presentActual_;
182  }
183  }
184  }
185  };
186 
187 } // namespace
188 
189 namespace edm::service::moduleAlloc {
190  Filter::Filter(std::vector<int> const* moduleIDs) : moduleIDs_{moduleIDs} {}
191 
192  bool Filter::startOnThread(int moduleID) const {
193  if (not globalKeep_.load()) {
194  return false;
195  }
196  if (keepModuleInfo(moduleID)) {
197  MonitorAdaptor::startOnThread();
198  return true;
199  }
200  return false;
201  }
202 
203  const ThreadAllocInfo* Filter::stopOnThread(int moduleID) const {
204  if (not globalKeep_.load()) {
205  return nullptr;
206  }
207 
208  if (keepModuleInfo(moduleID)) {
209  return &MonitorAdaptor::stopOnThread();
210  }
211  return nullptr;
212  }
213 
214  bool Filter::startOnThread() const {
215  if (not globalKeep_.load()) {
216  return false;
217  }
218  MonitorAdaptor::startOnThread();
219  return true;
220  }
221 
223  if (not globalKeep_.load()) {
224  return nullptr;
225  }
226  return &MonitorAdaptor::stopOnThread();
227  }
228 
229  void Filter::setGlobalKeep(bool iShouldKeep) { globalKeep_.store(iShouldKeep); }
230 
231  bool Filter::keepModuleInfo(int moduleID) const {
232  if ((nullptr == moduleIDs_) or (moduleIDs_->empty()) or
233  (std::binary_search(moduleIDs_->begin(), moduleIDs_->end(), moduleID))) {
234  return true;
235  }
236  return false;
237  }
238 } // namespace edm::service::moduleAlloc
239 
241 public:
243  : moduleNames_(iPS.getUntrackedParameter<std::vector<std::string>>("moduleNames")),
244  nEventsToSkip_(iPS.getUntrackedParameter<unsigned int>("nEventsToSkip")),
245  filter_(&moduleIDs_) {
247 
248  if (nEventsToSkip_ > 0) {
249  filter_.setGlobalKeep(false);
250  }
251  if (not moduleNames_.empty()) {
252  iAR.watchPreModuleConstruction([this](auto const& description) {
253  auto found = std::find(moduleNames_.begin(), moduleNames_.end(), description.moduleLabel());
254  if (found != moduleNames_.end()) {
255  moduleIDs_.push_back(description.id());
256  std::sort(moduleIDs_.begin(), moduleIDs_.end());
257  }
258  });
259 
260  iAR.watchPostESModuleRegistration([this](auto const& iDescription) {
261  auto label = iDescription.label_;
262  if (label.empty()) {
263  label = iDescription.type_;
264  }
265  auto found = std::find(moduleNames_.begin(), moduleNames_.end(), label);
266  if (found != moduleNames_.end()) {
267  //NOTE: we want the id to start at 1 not 0
268  moduleIDs_.push_back(-1 * (iDescription.id_ + 1));
269  std::sort(moduleIDs_.begin(), moduleIDs_.end());
270  }
271  });
272  }
273  if (nEventsToSkip_ > 0) {
274  iAR.watchPreSourceEvent([this](auto) {
275  ++nEventsStarted_;
276  if (nEventsStarted_ > nEventsToSkip_) {
277  filter_.setGlobalKeep(true);
278  }
279  });
280  }
282  }
283 
286  ps.addUntracked<std::string>("fileName");
287  ps.addUntracked<std::vector<std::string>>("moduleNames", std::vector<std::string>());
288  ps.addUntracked<unsigned int>("nEventsToSkip", 0);
289  iDesc.addDefault(ps);
290  }
291 
292 private:
293  bool forThisModule(unsigned int iID) {
294  return (moduleNames_.empty() or std::binary_search(moduleIDs_.begin(), moduleIDs_.end(), iID));
295  }
296  std::vector<std::string> moduleNames_;
297  std::vector<int> moduleIDs_;
298  unsigned int nEventsToSkip_ = 0;
299  std::atomic<unsigned int> nEventsStarted_{0};
301 };
302 
void watchPostESModuleRegistration(PostESModuleRegistration::slot_type const &iSlot)
ParameterDescriptionBase * addUntracked(U const &iLabel, T const &value)
void watchPreModuleConstruction(PreModuleConstruction::slot_type const &iSlot)
void find(edm::Handle< EcalRecHitCollection > &hits, DetId thisDet, std::vector< EcalRecHitCollection::const_iterator > &hit, bool debug=false)
Definition: FindCaloHit.cc:19
const ThreadAllocInfo * stopOnThread() const
TEMPL(T2) struct Divides void
Definition: Factorize.h:24
T getUntrackedParameter(std::string const &, T const &) const
bool forThisModule(unsigned int iID)
Filter(std::vector< int > const *moduleIDs)
ALPAKA_FN_HOST_ACC ALPAKA_FN_INLINE int binary_search(const unsigned int *data, unsigned int search_val, unsigned int ndata)
Definition: Hit.h:59
char const * label
static AllocMonitorRegistry & instance()
void setupFile(std::string const &iFileName, edm::ActivityRegistry &iRegistry, Filter const *iFilter)
void addDefault(ParameterSetDescription const &psetDescription)
The Signals That Services Can Subscribe To This is based on ActivityRegistry and is current per Services can connect to the signals distributed by the ActivityRegistry in order to monitor the activity of the application Each possible callback has some defined which we here list in angle e< void, edm::EventID const &, edm::Timestamp const & > We also list in braces which AR_WATCH_USING_METHOD_ is used for those or
Definition: Activities.doc:12
edm::service::moduleAlloc::Filter filter_
std::vector< int > moduleIDs_
bool keepModuleInfo(int moduleID) const
std::vector< std::string > moduleNames_
#define DEFINE_FWK_SERVICE(type)
Definition: ServiceMaker.h:97
std::vector< int > const * moduleIDs_
void watchPreSourceEvent(PreSourceEvent::slot_type const &iSlot)
ModuleAllocMonitor(edm::ParameterSet const &iPS, edm::ActivityRegistry &iAR)
static void fillDescriptions(edm::ConfigurationDescriptions &iDesc)