Grok 20.3.2
MemManager.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2016-2026 Grok Image Compression Inc.
3 *
4 * This source code is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License, version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This source code is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Affero General Public License for more details.
12 *
13 * You should have received a copy of the GNU Affero General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18#pragma once
19
20#include <cstdint>
21#include <cstddef>
22#include <cstdlib>
23#include <cassert>
24#include <atomic>
25#include <mutex>
26#include <cstdio>
27#include <unordered_map>
28
29#ifdef _WIN32
30#include <malloc.h>
31#include <windows.h>
32#elif defined(__linux__)
33#include <malloc.h>
34#endif
35
36#ifndef SIZE_MAX
37#define SIZE_MAX ((size_t)-1)
38#endif
39
40namespace grk
41{
42
43const size_t grk_buffer_alignment = 64;
44
45template<typename T>
46uint32_t grk_make_aligned_width(uint32_t width)
47{
48 assert(width);
49 assert(sizeof(T) <= grk_buffer_alignment);
50 size_t align = grk_buffer_alignment / sizeof(T);
51 return (uint32_t)((((uint64_t)width + align - 1) / align) * align);
52}
53
55{
56public:
58 {
59 static MemoryManager instance;
60 return instance;
61 }
62
63 static void releaseFreedPages()
64 {
65#ifdef __linux__
66 malloc_trim(0);
67#elif defined(_WIN32)
68 HeapCompact(GetProcessHeap(), 0);
69#endif
70 }
71
72 void* malloc(size_t size)
73 {
74 if(size == 0)
75 return nullptr;
76 void* ptr = std::malloc(size);
77 if(ptr && track_stats)
78 {
79 std::lock_guard<std::mutex> lock(stats_mutex);
81 total_allocated += size;
82 current_allocated += size;
85 allocation_map[ptr] = size;
86 }
87 return ptr;
88 }
89
90 void* calloc(size_t num, size_t size)
91 {
92 if(num == 0 || size == 0)
93 return nullptr;
94 void* ptr = std::calloc(num, size);
95 if(ptr && track_stats)
96 {
97 std::lock_guard<std::mutex> lock(stats_mutex);
99 size_t total_size = num * size;
100 total_allocated += total_size;
101 current_allocated += total_size;
103 if(track_details)
104 allocation_map[ptr] = total_size;
105 }
106 return ptr;
107 }
108
109 void* aligned_malloc(size_t bytes)
110 {
112 }
113
114 void* aligned_malloc(size_t alignment, size_t bytes)
115 {
116 assert((alignment != 0U) && ((alignment & (alignment - 1U)) == 0U));
117 assert(alignment >= sizeof(void*));
118
119 if(bytes == 0)
120 return nullptr;
121
122 bytes = ((bytes + alignment - 1) / alignment) * alignment;
123
124#ifdef _WIN32
125 void* ptr = _aligned_malloc(bytes, alignment);
126#else
127 void* ptr = std::aligned_alloc(alignment, bytes);
128#endif
129
130 if(ptr && track_stats)
131 {
132 std::lock_guard<std::mutex> lock(stats_mutex);
133 allocations++;
134 total_allocated += bytes;
135 current_allocated += bytes;
137 if(track_details)
138 allocation_map[ptr] = bytes;
139 }
140 return ptr;
141 }
142
143 void* realloc(void* ptr, size_t new_size)
144 {
145 if(new_size == 0)
146 return nullptr;
147
148 size_t old_size = 0;
149 bool was_allocated = false;
150 if(track_stats && ptr && track_details)
151 {
152 std::lock_guard<std::mutex> lock(stats_mutex);
153 auto it = allocation_map.find(ptr);
154 if(it != allocation_map.end())
155 {
156 old_size = it->second;
157 allocation_map.erase(it); // Erase before realloc
158 was_allocated = true;
159 }
160 }
161
162 void* new_ptr = std::realloc(ptr, new_size);
163 if(new_ptr && track_stats)
164 {
165 std::lock_guard<std::mutex> lock(stats_mutex);
166 if(ptr)
167 {
169 if(was_allocated)
170 current_allocated -= old_size;
171 }
172 else
173 {
174 allocations++;
175 }
176 current_allocated += new_size;
178 total_allocated += new_size;
179 if(track_details)
180 allocation_map[new_ptr] = new_size;
181 }
182 return new_ptr;
183 }
184
185 void free(void* ptr)
186 {
187 if(!ptr)
188 return;
189 if(track_stats)
190 {
191 std::lock_guard<std::mutex> lock(stats_mutex);
193 if(track_details)
194 {
195 auto it = allocation_map.find(ptr);
196 if(it != allocation_map.end())
197 {
198 current_allocated -= it->second;
199 allocation_map.erase(it);
200 }
201 }
202 }
203 std::free(ptr);
204 }
205
206 void aligned_free(void* ptr)
207 {
208 if(!ptr)
209 return;
210 if(track_stats)
211 {
212 std::lock_guard<std::mutex> lock(stats_mutex);
214 if(track_details)
215 {
216 auto it = allocation_map.find(ptr);
217 if(it != allocation_map.end())
218 {
219 current_allocated -= it->second;
220 allocation_map.erase(it);
221 }
222 }
223 }
224#ifdef _WIN32
225 _aligned_free(ptr);
226#else
227 std::free(ptr);
228#endif
229 }
230
231 struct Stats
232 {
233 size_t allocations = 0;
234 size_t deallocations = 0;
235 size_t reallocations = 0;
236 size_t total_allocated = 0;
238 size_t peak_allocated = 0;
239 };
240
242 {
243 std::lock_guard<std::mutex> lock(stats_mutex);
246 }
247
248 void print_stats() const
249 {
250 if(!track_stats)
251 return;
252
253 auto stats = get_stats();
254 double total_mb = static_cast<double>(stats.total_allocated) / (1024 * 1024);
255 double current_mb = static_cast<double>(stats.current_allocated) / (1024 * 1024);
256 double peak_mb = static_cast<double>(stats.peak_allocated) / (1024 * 1024);
257
258 std::printf("Memory Statistics:\n");
259 std::printf(" Allocations: %zu\n", stats.allocations);
260 std::printf(" Deallocations: %zu\n", stats.deallocations);
261 std::printf(" Reallocations: %zu\n", stats.reallocations);
262 std::printf(" Total Allocated: %.2f MB\n", total_mb);
263 std::printf(" Current Allocated: %.2f MB\n", current_mb);
264 std::printf(" Peak Allocated: %.2f MB\n", peak_mb);
265 std::printf(" Current Active Allocations: %zu\n", stats.allocations - stats.deallocations);
266 }
267
268private:
270 {
271 const char* debug_env = std::getenv("GRK_DEBUG");
272 track_stats = (debug_env && std::atoi(debug_env) == 5);
273 track_details = track_stats; // Could be separated with another env var if desired
274 }
275
277 {
278 if(track_stats)
279 {
280 print_stats();
281 }
282 }
283
284 MemoryManager(const MemoryManager&) = delete;
286
287 bool track_stats = false;
288 bool track_details = false; // Controls whether to use allocation_map
289 mutable std::mutex stats_mutex;
290 std::atomic<size_t> allocations{0};
291 std::atomic<size_t> deallocations{0};
292 std::atomic<size_t> reallocations{0};
293 std::atomic<size_t> total_allocated{0};
294 size_t current_allocated = 0; // Not atomic, protected by mutex
295 size_t peak_allocated = 0; // Not atomic, protected by mutex
296 std::unordered_map<void*, size_t> allocation_map; // Tracks size of each allocation
297};
298
299// Inline functions for convenience
300inline void* grk_malloc(size_t size)
301{
302 return MemoryManager::get().malloc(size);
303}
304inline void* grk_calloc(size_t num, size_t size)
305{
306 return MemoryManager::get().calloc(num, size);
307}
308inline void* grk_aligned_malloc(size_t bytes)
309{
310 return MemoryManager::get().aligned_malloc(bytes);
311}
312inline void* grk_aligned_malloc(size_t alignment, size_t bytes)
313{
314 return MemoryManager::get().aligned_malloc(alignment, bytes);
315}
316inline void* grk_realloc(void* ptr, size_t new_size)
317{
318 return MemoryManager::get().realloc(ptr, new_size);
319}
320inline void grk_free(void* ptr)
321{
323}
324inline void grk_aligned_free(void* ptr)
325{
327}
332
333} // namespace grk
void * realloc(void *ptr, size_t new_size)
Definition MemManager.h:143
Stats get_stats() const
Definition MemManager.h:241
static MemoryManager & get()
Definition MemManager.h:57
std::atomic< size_t > reallocations
Definition MemManager.h:292
static void releaseFreedPages()
Definition MemManager.h:63
size_t peak_allocated
Definition MemManager.h:295
std::unordered_map< void *, size_t > allocation_map
Definition MemManager.h:296
bool track_details
Definition MemManager.h:288
void * aligned_malloc(size_t bytes)
Definition MemManager.h:109
std::atomic< size_t > total_allocated
Definition MemManager.h:293
void * aligned_malloc(size_t alignment, size_t bytes)
Definition MemManager.h:114
MemoryManager()
Definition MemManager.h:269
std::mutex stats_mutex
Definition MemManager.h:289
void * malloc(size_t size)
Definition MemManager.h:72
MemoryManager & operator=(const MemoryManager &)=delete
void print_stats() const
Definition MemManager.h:248
void * calloc(size_t num, size_t size)
Definition MemManager.h:90
bool track_stats
Definition MemManager.h:287
void free(void *ptr)
Definition MemManager.h:185
~MemoryManager()
Definition MemManager.h:276
std::atomic< size_t > allocations
Definition MemManager.h:290
std::atomic< size_t > deallocations
Definition MemManager.h:291
size_t current_allocated
Definition MemManager.h:294
void aligned_free(void *ptr)
Definition MemManager.h:206
MemoryManager(const MemoryManager &)=delete
ResWindow.
Definition CompressedChunkCache.h:36
void grk_print_memory_stats()
Definition MemManager.h:328
const size_t grk_buffer_alignment
Definition MemManager.h:43
void * grk_calloc(size_t num, size_t size)
Definition MemManager.h:304
void * grk_aligned_malloc(size_t bytes)
Definition MemManager.h:308
uint32_t grk_make_aligned_width(uint32_t width)
Definition MemManager.h:46
void grk_free(void *ptr)
Definition MemManager.h:320
void * grk_malloc(size_t size)
Definition MemManager.h:300
void grk_aligned_free(void *ptr)
Definition MemManager.h:324
void * grk_realloc(void *ptr, size_t new_size)
Definition MemManager.h:316
Definition MemManager.h:232
size_t current_allocated
Definition MemManager.h:237
size_t allocations
Definition MemManager.h:233
size_t peak_allocated
Definition MemManager.h:238
size_t reallocations
Definition MemManager.h:235
size_t total_allocated
Definition MemManager.h:236
size_t deallocations
Definition MemManager.h:234