Skip to content

Commit fbeaff2

Browse files
committed
More ustring internals revision
* Coalesce repeated pool block allocation logic into allocate_pool_block(). * Change all_pools and large_allocs to vectors of std::unique_ptr so clearing the vec frees the pointers automatically. * shrink_to_fit() after clearing all_pools and large_allocs. * Initialize all TableRepMap fields by default, especially the pointers. * When in debug mode, print when freeing the ustring table resources, so we are sure it happens. Signed-off-by: Larry Gritz <lg@larrygritz.com>
1 parent f46a123 commit fbeaff2

1 file changed

Lines changed: 33 additions & 25 deletions

File tree

src/libutil/ustring.cpp

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,10 @@ template<unsigned BASE_CAPACITY, unsigned POOL_SIZE> struct TableRepMap {
4242
TableRepMap()
4343
: entries(static_cast<ustring::TableRep**>(
4444
calloc(BASE_CAPACITY, sizeof(ustring::TableRep*))))
45-
, pool(static_cast<char*>(malloc(POOL_SIZE)))
46-
, memory_usage(sizeof(*this) + POOL_SIZE
45+
, memory_usage(sizeof(*this)
4746
+ sizeof(ustring::TableRep*) * BASE_CAPACITY)
4847
{
49-
all_pools.push_back(pool);
48+
allocate_pool_block();
5049
}
5150

5251
~TableRepMap()
@@ -63,23 +62,19 @@ template<unsigned BASE_CAPACITY, unsigned POOL_SIZE> struct TableRepMap {
6362
entries[i]->~TableRep();
6463
}
6564
// Free all pool allocations and large individual allocations.
66-
for (char* p : all_pools)
67-
free(p);
6865
all_pools.clear();
69-
for (char* p : large_allocs)
70-
free(p);
66+
all_pools.shrink_to_fit();
7167
large_allocs.clear();
68+
large_allocs.shrink_to_fit();
7269
free(entries);
7370
// Re-initialize to a fresh, usable state.
7471
mask = BASE_CAPACITY - 1;
7572
entries = static_cast<ustring::TableRep**>(
7673
calloc(BASE_CAPACITY, sizeof(ustring::TableRep*)));
77-
pool = static_cast<char*>(malloc(POOL_SIZE));
78-
pool_offset = 0;
7974
num_entries = 0;
80-
memory_usage = sizeof(*this) + POOL_SIZE
75+
memory_usage = sizeof(*this)
8176
+ sizeof(ustring::TableRep*) * BASE_CAPACITY;
82-
all_pools.push_back(pool);
77+
allocate_pool_block();
8378
}
8479

8580
size_t get_memory_usage()
@@ -212,6 +207,9 @@ template<unsigned BASE_CAPACITY, unsigned POOL_SIZE> struct TableRepMap {
212207
return new (repmem) ustring::TableRep(str, hash);
213208
}
214209

210+
// Allocate `len` bytes from the pool. Allocate a new pool block if len
211+
// doesn't fit in the current block. In the unlikely even that len > the
212+
// pool block size, do a separate allocation just for it.
215213
char* pool_alloc(size_t len)
216214
{
217215
// round up to nearest multiple of pointer size to guarantee proper alignment of TableRep objects
@@ -220,30 +218,36 @@ template<unsigned BASE_CAPACITY, unsigned POOL_SIZE> struct TableRepMap {
220218

221219
if (len >= POOL_SIZE) {
222220
memory_usage += len;
223-
char* p = (char*)malloc(len);
224-
large_allocs.push_back(p);
221+
char* p = new char[len];
222+
large_allocs.emplace_back(p);
225223
return p;
226224
}
227225
if (pool_offset + len > POOL_SIZE) {
228-
memory_usage += POOL_SIZE;
229-
pool = (char*)malloc(POOL_SIZE);
230-
pool_offset = 0;
231-
all_pools.push_back(pool);
226+
allocate_pool_block();
232227
}
233228
char* result = pool + pool_offset;
234229
pool_offset += len;
235230
return result;
236231
}
237232

233+
// Allocate one more standard POOL_SIZE block for `pool`
234+
void allocate_pool_block()
235+
{
236+
memory_usage += POOL_SIZE;
237+
pool = new char[POOL_SIZE];
238+
pool_offset = 0;
239+
all_pools.emplace_back(pool);
240+
}
241+
238242
OIIO_CACHE_ALIGN mutable ustring_mutex_t mutex;
239-
size_t mask = BASE_CAPACITY - 1;
240-
ustring::TableRep** entries;
241-
size_t num_entries = 0;
242-
char* pool;
243-
size_t pool_offset = 0;
244-
size_t memory_usage;
245-
std::vector<char*> all_pools;
246-
std::vector<char*> large_allocs;
243+
size_t mask = BASE_CAPACITY - 1;
244+
ustring::TableRep** entries = nullptr;
245+
size_t num_entries = 0;
246+
char* pool = nullptr; // Current pool block we're using
247+
size_t pool_offset = 0; // Next offset within current block
248+
size_t memory_usage = 0; // Total memory usage
249+
std::vector<std::unique_ptr<char[]>> all_pools;
250+
std::vector<std::unique_ptr<char[]>> large_allocs;
247251
#ifdef USTRING_TRACK_NUM_LOOKUPS
248252
size_t num_lookups = 0;
249253
#endif
@@ -741,6 +745,10 @@ OIIO_UTIL_API int oiio_ustring_cleanup = Strutil::stoi(
741745
static int ustring_cleanup_atexit_registered = []() {
742746
std::atexit([]() {
743747
if (pvt::oiio_ustring_cleanup) {
748+
#ifndef NDEBUG
749+
OIIO::print("ustring: freeing table resources ({} bytes)\n",
750+
v3_1::ustring_table().get_memory_usage());
751+
#endif
744752
v3_1::ustring_table().clear();
745753
v3_1::reverse_map().clear();
746754
}

0 commit comments

Comments
 (0)