HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_MemoryTable.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_MemoryTable.h ( UT Library, C++)
7  *
8  * COMMENTS: Memory tracking utilities
9  */
10 
11 #ifndef __UT_MemoryTable__
12 #define __UT_MemoryTable__
13 
14 #include "UT_API.h"
15 #include <SYS/SYS_Types.h>
16 #include <SYS/SYS_Math.h>
17 #include <SYS/SYS_AtomicInt.h>
18 #include "UT_NonCopyable.h"
19 #include "UT_ThreadSpecificValue.h"
20 
21 #include <cstddef>
22 
23 /// Memory Tracking
24 ///
25 /// This class provides a mechanism for tracking memory usage. If all
26 /// memory allocation and freeing is registered with this class, then
27 /// the memory footprint and resource usage can be tracked.
28 ///
29 /// There is a companion class at UT which allows for printing of the memory
30 /// statistics.
31 ///
32 /// An example of how to use this might be:
33 ///
34 /// enum {
35 /// MEM_DEBUG = 0,
36 /// MEM_TASK1,
37 /// MEM_TASK2,
38 /// MEM_MAX_TASKS
39 /// };
40 ///
41 /// static UT_MemoryUser theMemoryUsers[MEM_MAX_TASKS] = {
42 /// UT_MemoryUser("Debug),
43 /// UT_MemoryUser("Task One"),
44 /// UT_MemoryUser("Task Two"),
45 /// };
46 ///
47 /// static UT_MemoryTable theMemoryTable(theMemoryUsers,
48 /// sizeof(theMemoryUsers)/sizeof(UT_MemoryTable));
49 ///
50 /// int
51 /// main(int argc, char *argv[])
52 /// {
53 /// UT_WorkBuffer stats;
54 /// void *mem;
55 /// mem = UT_Memory::Malloc(theMemoryTable, MEM_TASK1, 128);
56 /// mem = UT_Memory::Realloc(theMemoryTable, MEM_TASK1, mem, 128, 256);
57 /// UT_Memory::Free(theMemoryTable, MEM_TASK1, mem, 256);
58 /// UT_Memory::printTable(stats, theMemoryTable);
59 /// cout << stats.buffer();
60 /// return 0;
61 /// }
62 ///
63 
65  : public UT_NonCopyable
66 {
67  struct Counter
68  {
69  Counter(bool atomic)
70  : myUseAtomic(atomic)
71  {
72  set(0);
73  }
74  void set(size_t amount)
75  {
76  if (myUseAtomic)
77  myAtomic.exchange(amount);
78  else
79  {
80  for (auto &val : myValue)
81  {
82  val = amount;
83  amount = 0; // Clear all other values
84  }
85  }
86  }
87  size_t get() const
88  {
89  if (myUseAtomic)
90  return myAtomic.relaxedLoad();
91 
92  size_t sum = 0;
93  for (auto &val : myValue)
94  sum += val;
95  return sum;
96  }
97  size_t add(size_t amount)
98  {
99  if (myUseAtomic)
100  return myAtomic.add(amount);
101  auto &val = myValue.get();
102  val += amount;
103  return val;
104  }
105  size_t sub(size_t amount)
106  {
107  if (myUseAtomic)
108  return myAtomic.add(-amount);
109  auto &val = myValue.get();
110  val -= amount;
111  return val;
112  }
113  size_t max(size_t amount)
114  {
115  if (myUseAtomic)
116  return myAtomic.maximum(amount);
117  auto &val = myValue.get();
118  val = SYSmax(val, amount);
119  return val;
120  }
121  private:
122  SYS_AtomicCounter myAtomic;
124  bool myUseAtomic;
125  };
126 
127 public:
128  UT_MemoryUser(const char *label, bool use_atomics = false)
129  : myUsed(use_atomics)
130  , myPeak(use_atomics)
131  {
132  // WARNING: The label is a reference to the string. It's not hardened
133  myLabel = label;
134  }
135 
136  const char *getLabel() const { return myLabel; }
137 
138  /// Note that getUsed() and getPeak() may not be thread-safe unless
139  /// initialized with @c use_atomics = true
140  size_t getUsed() const { return myUsed.get(); }
141  size_t getPeak() const { return myPeak.get(); }
142  void inc(size_t amount) { myPeak.max(myUsed.add(amount)); }
143  void dec(size_t amount) { myUsed.sub(amount); }
144 
145  /// Forcibly set values - not typical usage, but required in some cases
146  void setUsedPeak(size_t used, size_t peak)
147  {
148  myUsed.set(used);
149  myPeak.set(peak);
150  }
151 
152 private:
153  const char *myLabel;
154  Counter myUsed;
155  Counter myPeak;
156 };
157 
159 {
160 public:
161  UT_MemoryTable(UT_MemoryUser *user_list, int number_of_users)
162  {
163  myTable = user_list;
164  mySize = number_of_users;
165  }
166 
167  UT_MemoryUser &getUser(int index) { return myTable[index]; }
168  const UT_MemoryUser &getUser(int index) const { return myTable[index]; }
169 
170  int entries() const { return mySize; }
171  size_t getUsed() const;
172  size_t getPeak() const;
173 
174  void inc(int user, size_t amount)
175  {
176  myTable[user].inc(amount);
177  }
178  void dec(int user, size_t amount)
179  {
180  myTable[user].dec(amount);
181  }
182 
183 private:
184  UT_MemoryUser *myTable;
185  int mySize;
186 };
187 
189 {
190 public:
191  /// Allocate memory and track the usage.
192  /// This method will return a null pointer if the size requested is equal
193  /// to 0.
194  static void *Malloc(UT_MemoryTable &table,
195  int which_entry,
196  size_t amount)
197  {
198  if (amount)
199  {
200  table.inc(which_entry, amount);
201  return malloc(amount);
202  }
203  return 0;
204  }
205  /// Allocate memory and track the usage. The memory will filled with zero.
206  /// This method will return a null pointer if the size requested is equal
207  /// to 0.
208  static void *Calloc(UT_MemoryTable &table,
209  int which_entry,
210  size_t amount)
211  {
212  if (amount)
213  {
214  table.inc(which_entry, amount);
215  return calloc(1, amount);
216  }
217  return 0;
218  }
219  /// Re-allocate a previously allocated block. This method handles
220  /// the case where the initial block has not been allocated (i.e. is a
221  /// null pointer).
222  /// This method will return a null pointer if the new_amount size requested
223  /// is equal to 0.
224  static void *Realloc(UT_MemoryTable &table,
225  int which_entry,
226  void *original_memory,
227  size_t old_amount,
228  size_t new_amount)
229  {
230  void *mem;
231  if (new_amount)
232  {
233  if (original_memory)
234  {
235  // Do it as two separate operations since size_t
236  // may be an unsigned and taking the delta may
237  // cause an overflow.
238  table.dec(which_entry, old_amount);
239  table.inc(which_entry, new_amount);
240  mem = realloc(original_memory, new_amount);
241  }
242  else
243  {
244  table.inc(which_entry, new_amount);
245  mem = malloc(new_amount);
246  }
247  }
248  else
249  {
250  mem = 0;
251  if (original_memory)
252  {
253  table.dec(which_entry, old_amount);
254  free(original_memory);
255  }
256  }
257  return mem;
258  }
259 
260  /// Free an allocated block (tracking the memory usage).
261  /// If a null pointer is passed in, no tracking will be done. Thus, it's
262  /// safe to pass in a non-zero size without a valid pointer (and not
263  /// destroy the tracking process).
264  static void Free(UT_MemoryTable &table,
265  int which_entry,
266  void *memory,
267  size_t amount)
268  {
269  if (memory)
270  {
271  table.dec(which_entry, amount);
272  free(memory);
273  }
274  }
275 
276  /// Track a memory acquisition that wasn't performed by this class.
278  int which_entry,
279  size_t amount)
280  {
281  table.inc(which_entry, amount);
282  }
283  /// Track a memory free that wasn't performed by this class.
285  int which_entry,
286  size_t amount)
287  {
288  table.dec(which_entry, amount);
289  }
290 };
291 
292 /// When implementing in a different library, the following macro can be used
293 /// to implement a localized interface. For example, we might have
294 /// UT_MEMORY_SUBCLASS(, GU, theGUMemoryTable)
295 /// or
296 /// class GU_Memory {
297 /// public:
298 /// UT_MEMORY_SUBCLASS(static, GU, myMemoryTable)
299 /// private:
300 /// static UT_MemoryTable myMemoryTable;
301 /// };
302 ///
303 #define UT_MEMORY_SUBCLASS(STATIC, prefix, table) \
304  STATIC void *prefix##Malloc(int i, size_t amount) \
305  { return UT_MemoryAPI::Malloc(table, i, amount); } \
306  STATIC void *prefix##Calloc(int i, size_t amount) \
307  { return UT_MemoryAPI::Calloc(table, i, amount); } \
308  STATIC void *prefix##Realloc(int i, void *m, size_t o, size_t n) \
309  { return UT_MemoryAPI::Realloc(table, i, m, o, n); } \
310  STATIC void prefix##Free(int i, void *m, size_t amount) \
311  { UT_MemoryAPI::Free(table, i, m, amount); } \
312  STATIC void prefix##acquire(int i, size_t amount) \
313  { UT_MemoryAPI::acquire(table, i, amount); } \
314  STATIC void prefix##release(int i, size_t amount) \
315  { UT_MemoryAPI::release(table, i, amount); } \
316  static inline UT_MemoryTable &prefix##getTable() { return table; }
317 
318 /// Convenience function to print memory in a consistent format (i.e. 12.3 KB).
319 /// To print "18446744073709551616.00 MB" requires 27 characters. There are 5
320 /// extra characters for good measure in UT_MEMPRINTSIZE
321 #define UT_MEMPRINTSIZE 32
323  int field_width=-1);
324 
325 #endif
GLsizei GLenum GLsizei GLsizei GLuint memory
Definition: RE_OGL.h:202
#define SYSmax(a, b)
Definition: SYS_Math.h:1952
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glcorearb.h:2540
const UT_MemoryUser & getUser(int index) const
void dec(size_t amount)
UT_MemoryUser & getUser(int index)
GLuint GLsizei const GLchar * label
Definition: glcorearb.h:2545
void inc(int user, size_t amount)
static void Free(UT_MemoryTable &table, int which_entry, void *memory, size_t amount)
const char * getLabel() const
#define UT_API
Definition: UT_API.h:14
static void * Malloc(UT_MemoryTable &table, int which_entry, size_t amount)
static void * Calloc(UT_MemoryTable &table, int which_entry, size_t amount)
#define UT_MEMPRINTSIZE
static void * Realloc(UT_MemoryTable &table, int which_entry, void *original_memory, size_t old_amount, size_t new_amount)
ImageBuf OIIO_API sub(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
static void acquire(UT_MemoryTable &table, int which_entry, size_t amount)
Track a memory acquisition that wasn't performed by this class.
constexpr auto set(type rhs) -> int
Definition: core.h:610
int entries() const
size_t getPeak() const
UT_MemoryUser(const char *label, bool use_atomics=false)
long long int64
Definition: SYS_Types.h:116
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
UT_MemoryTable(UT_MemoryUser *user_list, int number_of_users)
void setUsedPeak(size_t used, size_t peak)
Forcibly set values - not typical usage, but required in some cases.
UT_API void UTprintMemory(char buf[UT_MEMPRINTSIZE], int64 memory, int field_width=-1)
GLuint index
Definition: glcorearb.h:786
void dec(int user, size_t amount)
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
GLuint GLfloat * val
Definition: glcorearb.h:1608
void inc(size_t amount)
ImageBuf OIIO_API add(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
static void release(UT_MemoryTable &table, int which_entry, size_t amount)
Track a memory free that wasn't performed by this class.
size_t getUsed() const