HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_MemoryCounter.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_MemoryCounter.h (UT Library, C++)
7  *
8  * COMMENTS: Classes for simplifying counting of shared memory in
9  * different ways. There are currently 4 main use cases
10  * that require different methods of counting shared memory:
11  *
12  * 1) Counting the sum total of all used memory.
13  * 2) Counting how much memory would be freed if an object,
14  * such as a detail, is freed.
15  * 3) Counting the total amount of memory referenced by an
16  * object.
17  * 4) Counting the amount of memory referenced by an object
18  * that is not referenced by particular other objects.
19  */
20 
21 #ifndef __UT_MemoryCounter__
22 #define __UT_MemoryCounter__
23 
24 #include "UT_Assert.h"
25 #include "UT_ArrayMap.h"
26 #include "UT_ArraySet.h"
27 #include <SYS/SYS_Floor.h>
28 #include <SYS/SYS_Types.h>
29 
30 #define UT_MEMORY_DEBUG_LOGGING 0
31 #if !defined(UT_MEMORY_DEBUG_LOGGING) || UT_MEMORY_DEBUG_LOGGING==0
32 #define UT_MEMORY_DEBUG_LOG(m,s)
33 #define UT_MEMORY_DEBUG_LOG_SHARED(m,s,p,r)
34 #else
35 #define UT_MEMORY_DEBUG_LOG(m,s) {printf(m ": %" SYS_PRId64 " B\n", s); fflush(stdout);}
36 #define UT_MEMORY_DEBUG_LOG_SHARED(m,s,p,r) {printf(m ": %" SYS_PRId64 " B, 0x%p, refcount=%" SYS_PRId64 "\n", s,p,r); fflush(stdout);}
37 #endif
38 
39 /// This is the base class of a set of classes for simplifying counting
40 /// of shared memory for different use cases. The base class counts
41 /// all shared memory as if it is unshared, thus computing the total
42 /// amount of memory used by something, assuming that the same block
43 /// of memory is never referenced more than once.
45 {
46 public:
48  : myUnsharedCount(0)
49  , myMustCountShared(true)
50  , myMustCountUnshared(true)
51  {}
52 
53  /// NOTE: The virtual destructor is needed to destruct subclass
54  /// members, even if no special destruction is necessary
55  /// for the subclass itself.
56  virtual ~UT_MemoryCounter() {}
57 
58  /// This resets any data structures used for counting to their
59  /// freshly-constructed state.
60  /// NOTE: Subclasses should call UT_MemoryCounter::reset().
61  virtual void reset()
62  {
63  myUnsharedCount = 0;
64  }
65 
66  /// This counts shared memory according to the requirements of the
67  /// use case. The default implementation counts the total amount
68  /// of memory referenced by something, assuming the same block is
69  /// never referenced more than once.
70  ///
71  /// This returns true if the memory counter has recorded this shared
72  /// memory pointer at least once before. If you have a shared object
73  /// that itself owns separately shared data, if calling countShared
74  /// for the outer level of shared data returns true, the inner
75  /// level of shared data doesn't need to be counted again.
76  /// For example, when counting the memory of packed primitives,
77  /// multiple packed primitives may be referencing the same detail,
78  /// and multiple details may be sharing attribute data, so if
79  /// the countShared call for the detail handle returns true,
80  /// the detail doesn't need to be traversed again.
81  ///
82  /// NOTE: Subclasses should *NOT* call UT_MemoryCounter::countShared().
83  virtual bool countShared(size_t size, exint refcount, const void *p)
84  {
85  UT_ASSERT_P(refcount > 0);
86  myUnsharedCount += size;
87  return false;
88  }
89 
90  /// This counts unshared memory, which doesn't require any special
91  /// handling.
92  void countUnshared(size_t size)
93  {
94  myUnsharedCount += size;
95  }
96 
97  /// This returns the current count of memory used.
98  /// NOTE: Subclasses should call UT_MemoryCounter::getCount() to get
99  /// the unshared memory contribution.
100  virtual size_t getCount() const
101  {
102  return myUnsharedCount;
103  }
104 
105  /// If this returns false, the code using the
106  /// UT_MemoryCounter doesn't need to compute memory usage
107  /// for a complicated object if its refcount > 1.
108  ///
109  /// NOTE: Subclasses must handle shared memory appropriately,
110  /// even if this is false, to avoid having to always check this.
111  bool mustCountShared() const
112  {
113  return myMustCountShared;
114  }
115 
116  /// If this returns false, the code using the
117  /// UT_MemoryCounter doesn't need to compute memory usage
118  /// for a complicated object if it is unshared or has
119  /// refcount == 1.
120  ///
121  /// NOTE: Subclasses must handle memory with refcount==1 appropriately,
122  /// even if this is false, to avoid having to always check this.
123  bool mustCountUnshared() const
124  {
125  return myMustCountUnshared;
126  }
127 
128 protected:
129  UT_MemoryCounter(const bool countshared, const bool countunshared)
130  : myUnsharedCount(0)
131  , myMustCountShared(countshared)
132  , myMustCountUnshared(countunshared)
133  {}
134 
135 private:
136  /// The current count of unshared memory
137  size_t myUnsharedCount;
138 
139  /// true if this is interested in memory that is shared
140  /// with refcount > 1. If false, the code using the
141  /// UT_MemoryCounter doesn't need to compute memory usage
142  /// for a complicated object if its refcount > 1.
143  const bool myMustCountShared;
144 
145  /// true if this is interested in memory that is unshared
146  /// or has refcount == 1. If false, the code using the
147  /// UT_MemoryCounter doesn't need to compute memory usage
148  /// for a complicated object if it is unshared or has
149  /// refcount == 1.
150  const bool myMustCountUnshared;
151 };
152 
153 /// This subclass counts shared memory that is unique to something,
154 /// i.e. nothing else references the same memory blocks.
155 /// Since this class just uses the UT_MemoryCounter's unshared count,
156 /// it doesn't need to implement reset() or getCount().
158 {
159 public:
161  : UT_MemoryCounter(false, true)
162  {}
164 
165  /// This counts shared memory according to the requirements of the
166  /// use case. Since this use case only counts memory unique to
167  /// something, it only adds to the count if refcount==1.
168  bool countShared(size_t size, exint refcount, const void *p) override
169  {
170  UT_ASSERT_P(refcount > 0);
171  if (refcount == 1)
172  countUnshared(size);
173  return false;
174  }
175 };
176 
177 /// This subclass behaves the same as UT_MemoryCounterNewSafe,
178 /// except without an input set of pointers to avoid
180 {
181 public:
183  : UT_MemoryCounter(true, true)
184  , myVisitedCount()
185  , myFullSharedCount(0)
186  , myUniqueSharedCount(0)
187  {}
189 
190  /// This resets any data structures used for counting to their
191  /// freshly-constructed state.
192  void reset() override
193  {
195  myVisitedCount.clear();
196  myFullSharedCount = 0;
197  myUniqueSharedCount = 0;
198  }
199 
200  /// This counts shared memory according to the requirements of the
201  /// use case.
202  bool countShared(size_t size, exint refcount, const void *p) override
203  {
204  UT_ASSERT_P(refcount > 0);
205  UT_ASSERT_P(p);
206  // If refcount==1, it's always unique, so we don't have to
207  // add it to myVisitedCount
208  if (refcount == 1)
209  {
210  UT_ASSERT_MSG_P(myVisitedCount.count(p) == 0, "Why is p with refcount==1 referred to here more than once?");
211  UT_IF_ASSERT_P(myVisitedCount[p] = 1);
213  }
214  // If we haven't encountered it before, insert it
215  else if (myVisitedCount.count(p) == 0)
216  {
217  myVisitedCount[p] = 1;
218  myFullSharedCount += size;
219  }
220  else
221  {
222  exint newvisitcount = ++myVisitedCount[p];
223  // If all of the references to p are in what's being counted,
224  // it's unique to what's being counted.
225  if (newvisitcount == refcount)
226  myUniqueSharedCount += size;
227  UT_ASSERT_MSG_P(newvisitcount <= refcount, "Why is p referred to here more times than its refcount?");
228  return true;
229  }
230  return false;
231  }
232 
233  size_t getUniqueCount() const
234  {
235  return UT_MemoryCounter::getCount() + myUniqueSharedCount;
236  }
237 
238  size_t getFullCount() const
239  {
240  return UT_MemoryCounter::getCount() + myFullSharedCount;
241  }
242 
243 private:
244  UT::ArrayMap<const void *,exint> myVisitedCount;
245  size_t myFullSharedCount;
246  size_t myUniqueSharedCount;
247 };
248 
249 /// This subclass behaves the same as UT_MemoryCounterUniqueFullSafe,
250 /// except not checking whether memory is unique to what's being counted.
252 {
253 public:
255  : UT_MemoryCounter(true, true)
256  , myVisitedSet()
257  , myFullSharedCount(0)
258  {}
260 
261  /// This resets any data structures used for counting to their
262  /// freshly-constructed state.
263  void reset() override
264  {
266  myVisitedSet.clear();
267  myFullSharedCount = 0;
268  }
269 
270  /// This counts shared memory according to the requirements of the
271  /// use case.
272  bool countShared(size_t size, exint refcount, const void *p) override
273  {
274  UT_ASSERT_P(refcount > 0);
275  UT_ASSERT_P(p);
276  // If refcount==1, it's always unique, so we don't have to
277  // add it to myVisitedSet
278  if (refcount == 1)
279  {
280  UT_ASSERT_MSG_P(myVisitedSet.count(p) == 0, "Why is p with refcount==1 referred to here more than once?");
281  UT_IF_ASSERT_P(myVisitedSet.insert(p));
283  }
284  // If we haven't encountered it before, insert it
285  else if (myVisitedSet.count(p) == 0)
286  {
287  myVisitedSet.insert(p);
288  myFullSharedCount += size;
289  }
290  else
291  return true;
292  return false;
293  }
294 
295  size_t getFullCount() const
296  {
297  return UT_MemoryCounter::getCount() + myFullSharedCount;
298  }
299 
300 private:
301  UT::ArraySet<const void *> myVisitedSet;
302  size_t myFullSharedCount;
303 };
304 
305 /// This subclass counts shared memory under the assumption that
306 /// all memory is being counted, so on a block of memory referenced
307 /// refcount times, countShared() will be called refcount times.
308 /// To avoid double-counting of this shared memory, countShared()
309 /// divides by refcount.
311 {
312 public:
314  : UT_MemoryCounter(true, true)
315  , mySharedCount(0)
316  {}
318 
319  /// This resets any data structures used for counting to their
320  /// freshly-constructed state.
321  void reset() override
322  {
324  mySharedCount = 0;
325  }
326 
327  /// This counts shared memory according to the requirements of the
328  /// use case. This implementation avoids double-counting of
329  /// referenced memory by dividing size by the reference count.
330  bool countShared(size_t size, exint refcount, const void *p) override
331  {
332  UT_ASSERT_P(refcount > 0);
333  mySharedCount += fpreal(size)/refcount;
334  return false;
335  }
336 
337  /// This returns the current count of memory used.
338  size_t getCount() const override
339  {
340  return UT_MemoryCounter::getCount() + size_t(SYSrint(mySharedCount));
341  }
342 
343 private:
344  /// The current count of shared memory, each scaled by the reference
345  /// count to avoid double-counting.
346  fpreal mySharedCount;
347 };
348 
349 
350 /// This subclass counts shared memory excluding any shared memory
351 /// blocks in the specified set, so that one can determine how much
352 /// memory a node's output detail uses that its input details do not.
353 /// NOTE: This assumes that countShared() is called at most once for
354 /// each shared memory block, because unlike UT_MemoryCounterNewSafe,
355 /// it doesn't add each counted block to a set of blocks to avoid.
357 {
358 public:
360  : UT_MemoryCounter(true, true)
361  , myAvoidSet(avoidset)
362  , myNewSharedCount(0)
363  , myFullSharedCount(0)
364  , myUniqueCount(0)
365  {}
366  ~UT_MemoryCounterNew() override {}
367 
368  /// This resets any data structures used for counting to their
369  /// freshly-constructed state.
370  void reset() override
371  {
373  myNewSharedCount = 0;
374  myFullSharedCount = 0;
375  myUniqueCount = 0;
376  }
377 
378  /// This counts shared memory according to the requirements of the
379  /// use case. This implementation avoids counting any shared memory
380  /// in the specified set.
381  bool countShared(size_t size, exint refcount, const void *p) override
382  {
383  UT_ASSERT_P(refcount > 0);
384  UT_ASSERT_P(p);
385  myFullSharedCount += size;
386  if (myAvoidSet.count(p) == 0)
387  {
388  myNewSharedCount += size;
389  if (refcount == 1)
390  myUniqueCount += size;
391  }
392  else
393  {
394  UT_ASSERT_MSG_P(refcount > 1, "If we're the only one referring to this block, why is it in myAvoidSet?");
395  }
396  return false;
397  }
398 
399  /// This returns the current count of "new" memory used.
400  size_t getCount() const override
401  {
402  return UT_MemoryCounter::getCount() + myNewSharedCount;
403  }
404 
405  size_t getFullCount() const
406  {
407  return UT_MemoryCounter::getCount() + myFullSharedCount;
408  }
409 
410  size_t getUniqueCount() const
411  {
412  return UT_MemoryCounter::getCount() + myUniqueCount;
413  }
414 
415 private:
416  const UT::ArraySet<const void *> &myAvoidSet;
417  size_t myNewSharedCount;
418  size_t myUniqueCount;
419  size_t myFullSharedCount;
420 };
421 
422 /// This subclass counts shared memory excluding any shared memory
423 /// blocks in the specified set, so that one can determine how much
424 /// memory a node's output detail uses that its input details do not.
425 /// Unlike UT_MemoryCounterNew, this does *NOT* assume that the
426 /// countShared() is only called once for each shared memory block,
427 /// by adding all blocks to a dynamic set of blocks to avoid.
428 /// This incurs extra cost on every countShared().
430 {
431 public:
433  : UT_MemoryCounter(true, true)
434  , myAvoidSet(avoidset)
435  , myVisitedCount()
436  , myNewSharedCount(0)
437  , myFullSharedCount(0)
438  , myUniqueSharedCount(0)
439  {}
441 
442  /// This resets any data structures used for counting to their
443  /// freshly-constructed state.
444  void reset() override
445  {
447  myVisitedCount.clear();
448  myNewSharedCount = 0;
449  myFullSharedCount = 0;
450  myUniqueSharedCount = 0;
451  }
452 
453  /// This counts shared memory according to the requirements of the
454  /// use case. This implementation avoids counting any shared memory
455  /// in the specified set.
456  bool countShared(size_t size, exint refcount, const void *p) override
457  {
458  UT_ASSERT_P(refcount > 0);
459  UT_ASSERT_P(p);
460  // If refcount==1, it's always unique, so we don't have to
461  // add it to myVisitedCount
462  if (refcount == 1)
463  {
464  UT_ASSERT_MSG_P(myVisitedCount.count(p) == 0, "Why is p with refcount==1 referred to here more than once?");
465  UT_ASSERT_MSG_P(myAvoidSet.count(p) == 0, "Why is p with refcount==1 referred to here and in the input set?");
466  UT_IF_ASSERT_P(myVisitedCount[p] = 1);
468  }
469  // If we haven't encountered it before, insert it
470  else if (myVisitedCount.count(p) == 0)
471  {
472  myVisitedCount[p] = 1;
473  myFullSharedCount += size;
474  if (myAvoidSet.count(p) == 0)
475  myNewSharedCount += size;
476  }
477  else
478  {
479  exint newvisitcount = ++myVisitedCount[p];
480  // If all of the references to p are in what's being counted,
481  // it's unique to what's being counted.
482  if (newvisitcount == refcount)
483  {
484  UT_ASSERT_MSG_P(myAvoidSet.count(p) == 0, "Why does p have all refcount references here and also appear in the input set?");
485  myUniqueSharedCount += size;
486  }
487  UT_ASSERT_MSG_P(newvisitcount <= refcount, "Why is p referred to here more times than its refcount?");
488  return true;
489  }
490  return false;
491  }
492 
493  /// This returns the current count of memory used.
494  size_t getCount() const override
495  {
496  return UT_MemoryCounter::getCount() + myNewSharedCount;
497  }
498 
499  size_t getFullCount() const
500  {
501  return UT_MemoryCounter::getCount() + myFullSharedCount;
502  }
503 
504  size_t getUniqueCount() const
505  {
506  return UT_MemoryCounter::getCount() + myUniqueSharedCount;
507  }
508 
509 private:
510  const UT::ArraySet<const void *> &myAvoidSet;
511  UT::ArrayMap<const void *,exint> myVisitedCount;
512  size_t myNewSharedCount;
513  size_t myUniqueSharedCount;
514  size_t myFullSharedCount;
515 };
516 
517 /// This class is for gathering a set of shared memory blocks
518 /// using the same interface as memory counting, to avoid needing
519 /// two separate interfaces propagated to everything. This is intended
520 /// to be used before counting memory with UT_MemoryCounterNew or
521 /// UT_MemoryCounterNewSafe.
523 {
524 public:
526  : UT_MemoryCounter(true, false)
527  , myGatherSet(gatherset)
528  {}
530 
531  /// This resets any data structures used for counting to their
532  /// freshly-constructed state.
533  /// NOTE: This particular implementation does not necessarily
534  /// restore the gathered set to its initial state, if
535  /// it had elements to begin with.
536  void reset() override
537  {
539  myGatherSet.clear();
540  }
541 
542  /// This inserts the pointer p into the gathered set.
543  /// NOTE: If p is already there, it's not inserted again.
544  bool countShared(size_t size, exint refcount, const void *p) override
545  {
546  UT_ASSERT_P(refcount > 0);
547  UT_ASSERT_P(p);
548  // NOTE: If refcount==1, the block isn't shared with anything else,
549  // so we don't need to gather it.
550  if (refcount > 1)
551  myGatherSet.insert(p);
552  return false;
553  }
554 
555 private:
556  UT::ArraySet<const void *> &myGatherSet;
557 };
558 
559 
560 #endif
bool countShared(size_t size, exint refcount, const void *p) override
~UT_MemoryCounterTotal() override
void clear()
Definition: UT_ArraySet.h:368
std::pair< iterator, bool > insert(const value_type &value)
Definition: UT_ArraySet.h:964
bool countShared(size_t size, exint refcount, const void *p) override
bool countShared(size_t size, exint refcount, const void *p) override
~UT_MemoryCounterGather() override
int64 exint
Definition: SYS_Types.h:125
bool countShared(size_t size, exint refcount, const void *p) override
bool countShared(size_t size, exint refcount, const void *p) override
virtual bool countShared(size_t size, exint refcount, const void *p)
#define UT_ASSERT_MSG_P(ZZ,...)
Definition: UT_Assert.h:158
#define UT_IF_ASSERT_P(ZZ)
Definition: UT_Assert.h:182
UT_MemoryCounterNew(const UT::ArraySet< const void * > &avoidset)
size_t getFullCount() const
bool countShared(size_t size, exint refcount, const void *p) override
size_t getCount() const override
This returns the current count of memory used.
UT_MemoryCounter(const bool countshared, const bool countunshared)
size_t getUniqueCount() const
size_t getFullCount() const
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:155
size_t getUniqueCount() const
bool countShared(size_t size, exint refcount, const void *p) override
bool mustCountUnshared() const
~UT_MemoryCounterUnique() override
UT_MemoryCounterGather(UT::ArraySet< const void * > &gatherset)
size_t getCount() const override
This returns the current count of memory used.
void reset() override
virtual size_t getCount() const
fpreal32 SYSrint(fpreal32 val)
Definition: SYS_Floor.h:163
void countUnshared(size_t size)
virtual ~UT_MemoryCounter()
size_type count(const Key &key) const
Definition: UT_ArraySet.h:756
GLsizeiptr size
Definition: glcorearb.h:664
size_t getCount() const override
This returns the current count of "new" memory used.
size_type count(const Key &key) const
Definition: UT_ArrayMap.h:295
fpreal64 fpreal
Definition: SYS_Types.h:277
~UT_MemoryCounterNew() override
bool mustCountShared() const
virtual void reset()
UT_MemoryCounterNewSafe(const UT::ArraySet< const void * > &avoidset)
size_t getFullCount() const