Skip to content

Commit e0b4007

Browse files
committed
Add statistic to snmalloc.
This adds a collection of per sizeclass statistic for tracking how many allocations have occurred on each thread. These are racily combined to provide basic tracking information.
1 parent da51317 commit e0b4007

File tree

13 files changed

+594
-41
lines changed

13 files changed

+594
-41
lines changed

src/snmalloc/backend_helpers/statsrange.h

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ namespace snmalloc
1616
{
1717
using ContainsParent<ParentRange>::parent;
1818

19-
static inline stl::Atomic<size_t> current_usage{};
20-
static inline stl::Atomic<size_t> peak_usage{};
19+
static inline Stat usage{};
2120

2221
public:
2322
static constexpr bool Aligned = ParentRange::Aligned;
@@ -30,34 +29,26 @@ namespace snmalloc
3029

3130
CapPtr<void, ChunkBounds> alloc_range(size_t size)
3231
{
33-
auto result = parent.alloc_range(size);
34-
if (result != nullptr)
35-
{
36-
auto prev = current_usage.fetch_add(size);
37-
auto curr = peak_usage.load();
38-
while (curr < prev + size)
39-
{
40-
if (peak_usage.compare_exchange_weak(curr, prev + size))
41-
break;
42-
}
43-
}
44-
return result;
32+
auto r = parent.alloc_range(size);
33+
if (r != nullptr)
34+
usage += size;
35+
return r;
4536
}
4637

4738
void dealloc_range(CapPtr<void, ChunkBounds> base, size_t size)
4839
{
49-
current_usage -= size;
40+
usage -= size;
5041
parent.dealloc_range(base, size);
5142
}
5243

5344
size_t get_current_usage()
5445
{
55-
return current_usage.load();
46+
return usage.get_curr();
5647
}
5748

5849
size_t get_peak_usage()
5950
{
60-
return peak_usage.load();
51+
return usage.get_peak();
6152
}
6253
};
6354
};

src/snmalloc/ds_core/ds_core.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@
1616
#include "ptrwrap.h"
1717
#include "redblacktree.h"
1818
#include "seqset.h"
19-
#include "tid.h"
19+
#include "stats.h"
20+
#include "tid.h"

src/snmalloc/ds_core/stats.h

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#include "defines.h"
2+
3+
#include <atomic>
4+
#include <cstddef>
5+
6+
namespace snmalloc
7+
{
8+
/**
9+
* Very basic statistic that tracks current and peak values.
10+
*/
11+
class Stat
12+
{
13+
private:
14+
stl::Atomic<size_t> curr{0};
15+
stl::Atomic<size_t> peak{0};
16+
17+
public:
18+
void increase(size_t amount)
19+
{
20+
size_t c = (curr += amount);
21+
size_t p = peak.load(std::memory_order_relaxed);
22+
while (c > p)
23+
{
24+
if (peak.compare_exchange_strong(p, c))
25+
break;
26+
}
27+
}
28+
29+
void decrease(size_t amount)
30+
{
31+
size_t prev = curr.fetch_sub(amount);
32+
SNMALLOC_ASSERT_MSG(
33+
prev >= amount, "prev = {}, amount = {}", prev, amount);
34+
UNUSED(prev);
35+
}
36+
37+
size_t get_curr()
38+
{
39+
return curr.load(std::memory_order_relaxed);
40+
}
41+
42+
size_t get_peak()
43+
{
44+
return peak.load(std::memory_order_relaxed);
45+
}
46+
47+
void operator+=(size_t amount)
48+
{
49+
increase(amount);
50+
}
51+
52+
void operator-=(size_t amount)
53+
{
54+
decrease(amount);
55+
}
56+
57+
void operator++()
58+
{
59+
increase(1);
60+
}
61+
62+
void operator--()
63+
{
64+
decrease(1);
65+
}
66+
};
67+
68+
/**
69+
* Very basic statistic that can only grow. Not thread-safe.
70+
*/
71+
class MonotoneLocalStat
72+
{
73+
std::atomic<size_t> value{0};
74+
75+
public:
76+
void operator++(int)
77+
{
78+
value.fetch_add(1, std::memory_order_relaxed);
79+
}
80+
81+
void operator+=(const MonotoneLocalStat& other)
82+
{
83+
auto v = other.value.load(std::memory_order_relaxed);
84+
value.fetch_add(v, std::memory_order_relaxed);
85+
}
86+
87+
void operator+=(size_t v)
88+
{
89+
value.fetch_add(v, std::memory_order_relaxed);
90+
}
91+
92+
size_t operator*()
93+
{
94+
return value.load(std::memory_order_relaxed);
95+
}
96+
};
97+
} // namespace snmalloc

src/snmalloc/global/globalalloc.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ namespace snmalloc
8484
}
8585
}
8686

87+
if (result == nullptr)
88+
SNMALLOC_CHECK(RemoteDeallocCache<Config_>::remote_inflight.get_curr() == 0);
89+
8790
if (result != nullptr)
8891
{
8992
*result = okay;
@@ -128,6 +131,81 @@ namespace snmalloc
128131
}
129132
}
130133

134+
template<SNMALLOC_CONCEPT(IsConfig) Config_ = Config>
135+
inline static void get_stats(AllocStats& stats)
136+
{
137+
auto alloc = AllocPool<Config>::iterate();
138+
while (alloc != nullptr)
139+
{
140+
stats += alloc->get_stats();
141+
alloc = AllocPool<Config>::iterate(alloc);
142+
}
143+
}
144+
145+
template<SNMALLOC_CONCEPT(IsConfig) Config_ = Config>
146+
inline static void print_alloc_stats()
147+
{
148+
static std::atomic<size_t> dump{0};
149+
150+
auto l_dump = dump++;
151+
if (l_dump == 0)
152+
{
153+
message<1024>(
154+
"snmalloc_allocs,dumpid,sizeclass,size,allocated,deallocated,in_use,"
155+
"bytes,slabs allocated,slabs deallocated,slabs in_use,slabs bytes");
156+
message<1024>(
157+
"snmalloc_totals,dumpid,backend bytes,peak backend "
158+
"bytes,requested,slabs requested bytes,remote inflight bytes,allocator "
159+
"count");
160+
}
161+
162+
AllocStats stats;
163+
snmalloc::get_stats<Config>(stats);
164+
size_t total_live{0};
165+
size_t total_live_slabs{0};
166+
for (size_t i = 0; i < snmalloc::SIZECLASS_REP_SIZE; i++)
167+
{
168+
auto sc = snmalloc::sizeclass_t::from_raw(i);
169+
auto allocated = *stats[sc].objects_allocated;
170+
auto deallocated = *stats[sc].objects_deallocated;
171+
auto slabs_allocated = *stats[sc].slabs_allocated;
172+
auto slabs_deallocated = *stats[sc].slabs_deallocated;
173+
if (allocated == 0 && deallocated == 0)
174+
continue;
175+
auto size = snmalloc::sizeclass_full_to_size(sc);
176+
auto slab_size = snmalloc::sizeclass_full_to_slab_size(sc);
177+
auto in_use = allocated - deallocated;
178+
auto amount = in_use * size;
179+
total_live += amount;
180+
auto in_use_slabs = slabs_allocated - slabs_deallocated;
181+
auto amount_slabs = in_use_slabs * slab_size;
182+
total_live_slabs += amount_slabs;
183+
184+
snmalloc::message<1024>(
185+
"snmalloc_allocs,{},{},{},{},{},{},{},{},{},{},{}",
186+
l_dump,
187+
i,
188+
size,
189+
allocated,
190+
deallocated,
191+
in_use,
192+
amount,
193+
slabs_allocated,
194+
slabs_deallocated,
195+
in_use_slabs,
196+
amount_slabs);
197+
}
198+
snmalloc::message<1024>(
199+
"snmalloc_totals,{},{},{},{},{},{},{}",
200+
l_dump,
201+
Config::Backend::get_current_usage(),
202+
Config::Backend::get_peak_usage(),
203+
total_live,
204+
total_live_slabs,
205+
RemoteDeallocCache<Config>::remote_inflight.get_curr(),
206+
Config::pool().get_count());
207+
}
208+
131209
/**
132210
* Returns the number of remaining bytes in an object.
133211
*

0 commit comments

Comments
 (0)