|
|
|
@ -30,6 +30,8 @@ |
|
|
|
|
|
|
|
|
|
#include <google/protobuf/arenaz_sampler.h> |
|
|
|
|
|
|
|
|
|
#include <atomic> |
|
|
|
|
#include <limits> |
|
|
|
|
#include <memory> |
|
|
|
|
#include <random> |
|
|
|
|
#include <utility> |
|
|
|
@ -37,7 +39,6 @@ |
|
|
|
|
|
|
|
|
|
#include <gmock/gmock.h> |
|
|
|
|
#include <gtest/gtest.h> |
|
|
|
|
#include <google/protobuf/stubs/strutil.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Must be included last.
|
|
|
|
@ -57,10 +58,17 @@ class ThreadSafeArenaStatsHandlePeer { |
|
|
|
|
return h->info_; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
std::vector<size_t> GetBytesAllocated(ThreadSafeArenazSampler* s) { |
|
|
|
|
std::vector<size_t> res; |
|
|
|
|
s->Iterate([&](const ThreadSafeArenaStats& info) { |
|
|
|
|
res.push_back(info.bytes_allocated.load(std::memory_order_acquire)); |
|
|
|
|
for (const auto& block_stats : info.block_histogram) { |
|
|
|
|
size_t bytes_allocated = |
|
|
|
|
block_stats.bytes_allocated.load(std::memory_order_acquire); |
|
|
|
|
if (bytes_allocated != 0) { |
|
|
|
|
res.push_back(bytes_allocated); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
@ -69,7 +77,8 @@ ThreadSafeArenaStats* Register(ThreadSafeArenazSampler* s, size_t size, |
|
|
|
|
int64_t stride) { |
|
|
|
|
auto* info = s->Register(stride); |
|
|
|
|
assert(info != nullptr); |
|
|
|
|
info->bytes_allocated.store(size); |
|
|
|
|
info->block_histogram[0].bytes_allocated.store(size, |
|
|
|
|
std::memory_order_relaxed); |
|
|
|
|
return info; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -85,46 +94,100 @@ TEST(ThreadSafeArenaStatsTest, PrepareForSampling) { |
|
|
|
|
MutexLock l(&info.init_mu); |
|
|
|
|
info.PrepareForSampling(kTestStride); |
|
|
|
|
|
|
|
|
|
EXPECT_EQ(info.num_allocations.load(), 0); |
|
|
|
|
EXPECT_EQ(info.bytes_used.load(), 0); |
|
|
|
|
EXPECT_EQ(info.bytes_allocated.load(), 0); |
|
|
|
|
EXPECT_EQ(info.bytes_wasted.load(), 0); |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(), 0); |
|
|
|
|
for (const auto& block_stats : info.block_histogram) { |
|
|
|
|
EXPECT_EQ(block_stats.num_allocations.load(std::memory_order_relaxed), 0); |
|
|
|
|
EXPECT_EQ(block_stats.bytes_used.load(std::memory_order_relaxed), 0); |
|
|
|
|
EXPECT_EQ(block_stats.bytes_allocated.load(std::memory_order_relaxed), 0); |
|
|
|
|
EXPECT_EQ(block_stats.bytes_wasted.load(std::memory_order_relaxed), 0); |
|
|
|
|
} |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 0); |
|
|
|
|
EXPECT_EQ(info.weight, kTestStride); |
|
|
|
|
|
|
|
|
|
info.num_allocations.store(1, std::memory_order_relaxed); |
|
|
|
|
info.bytes_used.store(1, std::memory_order_relaxed); |
|
|
|
|
info.bytes_allocated.store(1, std::memory_order_relaxed); |
|
|
|
|
info.bytes_wasted.store(1, std::memory_order_relaxed); |
|
|
|
|
for (auto& block_stats : info.block_histogram) { |
|
|
|
|
block_stats.num_allocations.store(1, std::memory_order_relaxed); |
|
|
|
|
block_stats.bytes_used.store(1, std::memory_order_relaxed); |
|
|
|
|
block_stats.bytes_allocated.store(1, std::memory_order_relaxed); |
|
|
|
|
block_stats.bytes_wasted.store(1, std::memory_order_relaxed); |
|
|
|
|
} |
|
|
|
|
info.max_block_size.store(1, std::memory_order_relaxed); |
|
|
|
|
|
|
|
|
|
info.PrepareForSampling(2 * kTestStride); |
|
|
|
|
EXPECT_EQ(info.num_allocations.load(), 0); |
|
|
|
|
EXPECT_EQ(info.bytes_used.load(), 0); |
|
|
|
|
EXPECT_EQ(info.bytes_allocated.load(), 0); |
|
|
|
|
EXPECT_EQ(info.bytes_wasted.load(), 0); |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(), 0); |
|
|
|
|
for (auto& block_stats : info.block_histogram) { |
|
|
|
|
EXPECT_EQ(block_stats.num_allocations.load(std::memory_order_relaxed), 0); |
|
|
|
|
EXPECT_EQ(block_stats.bytes_used.load(std::memory_order_relaxed), 0); |
|
|
|
|
EXPECT_EQ(block_stats.bytes_allocated.load(std::memory_order_relaxed), 0); |
|
|
|
|
EXPECT_EQ(block_stats.bytes_wasted.load(std::memory_order_relaxed), 0); |
|
|
|
|
} |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 0); |
|
|
|
|
EXPECT_EQ(info.weight, 2 * kTestStride); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST(ThreadSafeArenaStatsTest, FindBin) { |
|
|
|
|
size_t current_bin = 0; |
|
|
|
|
size_t bytes = 1; |
|
|
|
|
while (current_bin < ThreadSafeArenaStats::kBlockHistogramBins - 1) { |
|
|
|
|
size_t next_bin = ThreadSafeArenaStats::FindBin(bytes); |
|
|
|
|
if (next_bin != current_bin) { |
|
|
|
|
// Test the bins increase linearly.
|
|
|
|
|
EXPECT_EQ(next_bin, current_bin + 1); |
|
|
|
|
// Test the bins change only at values of the form 2^k + 1.
|
|
|
|
|
EXPECT_EQ(absl::popcount(bytes - 1), 1); |
|
|
|
|
current_bin = next_bin; |
|
|
|
|
} |
|
|
|
|
++bytes; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST(ThreadSafeArenaStatsTest, MinMaxBlockSizeForBin) { |
|
|
|
|
std::pair<size_t, size_t> current_limits = |
|
|
|
|
ThreadSafeArenaStats::MinMaxBlockSizeForBin(0); |
|
|
|
|
EXPECT_EQ(current_limits.first, 1); |
|
|
|
|
EXPECT_LT(current_limits.first, current_limits.second); |
|
|
|
|
for (size_t i = 1; i < ThreadSafeArenaStats::kBlockHistogramBins; ++i) { |
|
|
|
|
std::pair<size_t, size_t> next_limits = |
|
|
|
|
ThreadSafeArenaStats::MinMaxBlockSizeForBin(i); |
|
|
|
|
EXPECT_LT(next_limits.first, next_limits.second); |
|
|
|
|
// Test the limits do not have gaps.
|
|
|
|
|
EXPECT_EQ(next_limits.first, current_limits.second + 1); |
|
|
|
|
if (i != ThreadSafeArenaStats::kBlockHistogramBins - 1) { |
|
|
|
|
EXPECT_EQ(next_limits.second, 2 * current_limits.second); |
|
|
|
|
} |
|
|
|
|
current_limits = next_limits; |
|
|
|
|
} |
|
|
|
|
// Test the limits cover the entire range possible.
|
|
|
|
|
EXPECT_EQ(current_limits.second, std::numeric_limits<size_t>::max()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST(ThreadSafeArenaStatsTest, RecordAllocateSlow) { |
|
|
|
|
ThreadSafeArenaStats info; |
|
|
|
|
constexpr int64_t kTestStride = 458; |
|
|
|
|
MutexLock l(&info.init_mu); |
|
|
|
|
info.PrepareForSampling(kTestStride); |
|
|
|
|
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/128, /*wasted=*/0); |
|
|
|
|
EXPECT_EQ(info.num_allocations.load(), 1); |
|
|
|
|
EXPECT_EQ(info.bytes_used.load(), 100); |
|
|
|
|
EXPECT_EQ(info.bytes_allocated.load(), 128); |
|
|
|
|
EXPECT_EQ(info.bytes_wasted.load(), 0); |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(), 128); |
|
|
|
|
RecordAllocateSlow(&info, /*requested=*/0, /*allocated=*/128, /*wasted=*/0); |
|
|
|
|
EXPECT_EQ( |
|
|
|
|
info.block_histogram[0].num_allocations.load(std::memory_order_relaxed), |
|
|
|
|
1); |
|
|
|
|
EXPECT_EQ(info.block_histogram[0].bytes_used.load(std::memory_order_relaxed), |
|
|
|
|
0); |
|
|
|
|
EXPECT_EQ( |
|
|
|
|
info.block_histogram[0].bytes_allocated.load(std::memory_order_relaxed), |
|
|
|
|
128); |
|
|
|
|
EXPECT_EQ( |
|
|
|
|
info.block_histogram[0].bytes_wasted.load(std::memory_order_relaxed), 0); |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 128); |
|
|
|
|
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/256, |
|
|
|
|
/*wasted=*/28); |
|
|
|
|
EXPECT_EQ(info.num_allocations.load(), 2); |
|
|
|
|
EXPECT_EQ(info.bytes_used.load(), 200); |
|
|
|
|
EXPECT_EQ(info.bytes_allocated.load(), 384); |
|
|
|
|
EXPECT_EQ(info.bytes_wasted.load(), 28); |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(), 256); |
|
|
|
|
EXPECT_EQ(info.block_histogram[0].bytes_used.load(std::memory_order_relaxed), |
|
|
|
|
100); |
|
|
|
|
EXPECT_EQ( |
|
|
|
|
info.block_histogram[0].bytes_wasted.load(std::memory_order_relaxed), 28); |
|
|
|
|
EXPECT_EQ( |
|
|
|
|
info.block_histogram[1].num_allocations.load(std::memory_order_relaxed), |
|
|
|
|
1); |
|
|
|
|
EXPECT_EQ( |
|
|
|
|
info.block_histogram[1].bytes_allocated.load(std::memory_order_relaxed), |
|
|
|
|
256); |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 256); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST(ThreadSafeArenaStatsTest, RecordAllocateSlowMaxBlockSizeTest) { |
|
|
|
@ -133,13 +196,13 @@ TEST(ThreadSafeArenaStatsTest, RecordAllocateSlowMaxBlockSizeTest) { |
|
|
|
|
MutexLock l(&info.init_mu); |
|
|
|
|
info.PrepareForSampling(kTestStride); |
|
|
|
|
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/128, /*wasted=*/0); |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(), 128); |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 128); |
|
|
|
|
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/256, |
|
|
|
|
/*wasted=*/28); |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(), 256); |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 256); |
|
|
|
|
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/128, |
|
|
|
|
/*wasted=*/28); |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(), 256); |
|
|
|
|
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 256); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST(ThreadSafeArenazSamplerTest, SamplingCorrectness) { |
|
|
|
@ -212,12 +275,15 @@ TEST(ThreadSafeArenazSamplerTest, Handle) { |
|
|
|
|
constexpr int64_t kTestStride = 17; |
|
|
|
|
ThreadSafeArenaStatsHandle h(sampler.Register(kTestStride)); |
|
|
|
|
auto* info = ThreadSafeArenaStatsHandlePeer::GetInfo(&h); |
|
|
|
|
info->bytes_allocated.store(0x12345678, std::memory_order_relaxed); |
|
|
|
|
info->block_histogram[0].bytes_allocated.store(0x12345678, |
|
|
|
|
std::memory_order_relaxed); |
|
|
|
|
|
|
|
|
|
bool found = false; |
|
|
|
|
sampler.Iterate([&](const ThreadSafeArenaStats& h) { |
|
|
|
|
if (&h == info) { |
|
|
|
|
EXPECT_EQ(h.bytes_allocated.load(), 0x12345678); |
|
|
|
|
EXPECT_EQ( |
|
|
|
|
h.block_histogram[0].bytes_allocated.load(std::memory_order_relaxed), |
|
|
|
|
0x12345678); |
|
|
|
|
EXPECT_EQ(h.weight, kTestStride); |
|
|
|
|
found = true; |
|
|
|
|
} |
|
|
|
@ -230,7 +296,8 @@ TEST(ThreadSafeArenazSamplerTest, Handle) { |
|
|
|
|
if (&h == info) { |
|
|
|
|
// this will only happen if some other thread has resurrected the info
|
|
|
|
|
// the old handle was using.
|
|
|
|
|
if (h.bytes_allocated.load() == 0x12345678) { |
|
|
|
|
if (h.block_histogram[0].bytes_allocated.load( |
|
|
|
|
std::memory_order_relaxed) == 0x12345678) { |
|
|
|
|
found = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -246,7 +313,7 @@ TEST(ThreadSafeArenazSamplerTest, Registration) { |
|
|
|
|
|
|
|
|
|
auto* info2 = Register(&sampler, 2, kTestStride); |
|
|
|
|
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 2)); |
|
|
|
|
info1->bytes_allocated.store(3); |
|
|
|
|
info1->block_histogram[0].bytes_allocated.store(3, std::memory_order_relaxed); |
|
|
|
|
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(3, 2)); |
|
|
|
|
|
|
|
|
|
sampler.Unregister(info1); |
|
|
|
@ -258,18 +325,18 @@ TEST(ThreadSafeArenazSamplerTest, Unregistration) { |
|
|
|
|
std::vector<ThreadSafeArenaStats*> infos; |
|
|
|
|
constexpr int64_t kTestStride = 200; |
|
|
|
|
for (size_t i = 0; i < 3; ++i) { |
|
|
|
|
infos.push_back(Register(&sampler, i, kTestStride)); |
|
|
|
|
infos.push_back(Register(&sampler, i + 1, kTestStride)); |
|
|
|
|
} |
|
|
|
|
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(0, 1, 2)); |
|
|
|
|
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 2, 3)); |
|
|
|
|
|
|
|
|
|
sampler.Unregister(infos[1]); |
|
|
|
|
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(0, 2)); |
|
|
|
|
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 3)); |
|
|
|
|
|
|
|
|
|
infos.push_back(Register(&sampler, 3, kTestStride)); |
|
|
|
|
infos.push_back(Register(&sampler, 4, kTestStride)); |
|
|
|
|
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(0, 2, 3, 4)); |
|
|
|
|
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 3, 3, 4)); |
|
|
|
|
sampler.Unregister(infos[3]); |
|
|
|
|
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(0, 2, 4)); |
|
|
|
|
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 3, 4)); |
|
|
|
|
|
|
|
|
|
sampler.Unregister(infos[0]); |
|
|
|
|
sampler.Unregister(infos[2]); |
|
|
|
|