Commit 48abb9fe by Chris Kennelly Committed by Copybara-Service

Record sizeof(key_type), sizeof(value_type) in hashtable profiles.

This can identify situations where flat_hash_* is suboptimal for large
elements.

PiperOrigin-RevId: 618937993
Change-Id: I2bde069bc3526b14ad1718ba6f50467002aeed16
parent 06e11906
...@@ -79,6 +79,8 @@ HashtablezInfo::~HashtablezInfo() = default; ...@@ -79,6 +79,8 @@ HashtablezInfo::~HashtablezInfo() = default;
void HashtablezInfo::PrepareForSampling(int64_t stride, void HashtablezInfo::PrepareForSampling(int64_t stride,
size_t inline_element_size_value, size_t inline_element_size_value,
size_t key_size_value,
size_t value_size_value,
uint16_t soo_capacity_value) { uint16_t soo_capacity_value) {
capacity.store(0, std::memory_order_relaxed); capacity.store(0, std::memory_order_relaxed);
size.store(0, std::memory_order_relaxed); size.store(0, std::memory_order_relaxed);
...@@ -99,6 +101,8 @@ void HashtablezInfo::PrepareForSampling(int64_t stride, ...@@ -99,6 +101,8 @@ void HashtablezInfo::PrepareForSampling(int64_t stride,
depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth, depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth,
/* skip_count= */ 0); /* skip_count= */ 0);
inline_element_size = inline_element_size_value; inline_element_size = inline_element_size_value;
key_size = key_size_value;
value_size = value_size_value;
soo_capacity = soo_capacity_value; soo_capacity = soo_capacity_value;
} }
...@@ -123,12 +127,13 @@ static bool ShouldForceSampling() { ...@@ -123,12 +127,13 @@ static bool ShouldForceSampling() {
} }
HashtablezInfo* SampleSlow(SamplingState& next_sample, HashtablezInfo* SampleSlow(SamplingState& next_sample,
size_t inline_element_size, uint16_t soo_capacity) { size_t inline_element_size, size_t key_size,
size_t value_size, uint16_t soo_capacity) {
if (ABSL_PREDICT_FALSE(ShouldForceSampling())) { if (ABSL_PREDICT_FALSE(ShouldForceSampling())) {
next_sample.next_sample = 1; next_sample.next_sample = 1;
const int64_t old_stride = exchange(next_sample.sample_stride, 1); const int64_t old_stride = exchange(next_sample.sample_stride, 1);
HashtablezInfo* result = GlobalHashtablezSampler().Register( HashtablezInfo* result = GlobalHashtablezSampler().Register(
old_stride, inline_element_size, soo_capacity); old_stride, inline_element_size, key_size, value_size, soo_capacity);
return result; return result;
} }
...@@ -158,11 +163,12 @@ HashtablezInfo* SampleSlow(SamplingState& next_sample, ...@@ -158,11 +163,12 @@ HashtablezInfo* SampleSlow(SamplingState& next_sample,
// that case. // that case.
if (first) { if (first) {
if (ABSL_PREDICT_TRUE(--next_sample.next_sample > 0)) return nullptr; if (ABSL_PREDICT_TRUE(--next_sample.next_sample > 0)) return nullptr;
return SampleSlow(next_sample, inline_element_size, soo_capacity); return SampleSlow(next_sample, inline_element_size, key_size, value_size,
soo_capacity);
} }
return GlobalHashtablezSampler().Register(old_stride, inline_element_size, return GlobalHashtablezSampler().Register(old_stride, inline_element_size,
soo_capacity); key_size, value_size, soo_capacity);
#endif #endif
} }
......
...@@ -73,6 +73,7 @@ struct HashtablezInfo : public profiling_internal::Sample<HashtablezInfo> { ...@@ -73,6 +73,7 @@ struct HashtablezInfo : public profiling_internal::Sample<HashtablezInfo> {
// Puts the object into a clean state, fills in the logically `const` members, // Puts the object into a clean state, fills in the logically `const` members,
// blocking for any readers that are currently sampling the object. // blocking for any readers that are currently sampling the object.
void PrepareForSampling(int64_t stride, size_t inline_element_size_value, void PrepareForSampling(int64_t stride, size_t inline_element_size_value,
size_t key_size, size_t value_size,
uint16_t soo_capacity_value) uint16_t soo_capacity_value)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu); ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu);
...@@ -104,6 +105,8 @@ struct HashtablezInfo : public profiling_internal::Sample<HashtablezInfo> { ...@@ -104,6 +105,8 @@ struct HashtablezInfo : public profiling_internal::Sample<HashtablezInfo> {
uint16_t soo_capacity; uint16_t soo_capacity;
void* stack[kMaxStackDepth]; void* stack[kMaxStackDepth];
size_t inline_element_size; // How big is the slot in bytes? size_t inline_element_size; // How big is the slot in bytes?
size_t key_size; // sizeof(key_type)
size_t value_size; // sizeof(value_type)
}; };
void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length); void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length);
...@@ -128,7 +131,8 @@ struct SamplingState { ...@@ -128,7 +131,8 @@ struct SamplingState {
}; };
HashtablezInfo* SampleSlow(SamplingState& next_sample, HashtablezInfo* SampleSlow(SamplingState& next_sample,
size_t inline_element_size, uint16_t soo_capacity); size_t inline_element_size, size_t key_size,
size_t value_size, uint16_t soo_capacity);
void UnsampleSlow(HashtablezInfo* info); void UnsampleSlow(HashtablezInfo* info);
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
...@@ -218,13 +222,16 @@ extern ABSL_PER_THREAD_TLS_KEYWORD SamplingState global_next_sample; ...@@ -218,13 +222,16 @@ extern ABSL_PER_THREAD_TLS_KEYWORD SamplingState global_next_sample;
// Returns a sampling handle. // Returns a sampling handle.
inline HashtablezInfoHandle Sample( inline HashtablezInfoHandle Sample(
ABSL_ATTRIBUTE_UNUSED size_t inline_element_size, ABSL_ATTRIBUTE_UNUSED size_t inline_element_size,
ABSL_ATTRIBUTE_UNUSED size_t key_size,
ABSL_ATTRIBUTE_UNUSED size_t value_size,
ABSL_ATTRIBUTE_UNUSED uint16_t soo_capacity) { ABSL_ATTRIBUTE_UNUSED uint16_t soo_capacity) {
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
if (ABSL_PREDICT_TRUE(--global_next_sample.next_sample > 0)) { if (ABSL_PREDICT_TRUE(--global_next_sample.next_sample > 0)) {
return HashtablezInfoHandle(nullptr); return HashtablezInfoHandle(nullptr);
} }
return HashtablezInfoHandle( return HashtablezInfoHandle(SampleSlow(global_next_sample,
SampleSlow(global_next_sample, inline_element_size, soo_capacity)); inline_element_size, key_size,
value_size, soo_capacity));
#else #else
return HashtablezInfoHandle(nullptr); return HashtablezInfoHandle(nullptr);
#endif // !ABSL_PER_THREAD_TLS #endif // !ABSL_PER_THREAD_TLS
......
...@@ -1786,7 +1786,8 @@ constexpr bool ShouldSampleHashtablezInfo() { ...@@ -1786,7 +1786,8 @@ constexpr bool ShouldSampleHashtablezInfo() {
} }
template <bool kSooEnabled> template <bool kSooEnabled>
HashtablezInfoHandle SampleHashtablezInfo(size_t sizeof_slot, HashtablezInfoHandle SampleHashtablezInfo(size_t sizeof_slot, size_t sizeof_key,
size_t sizeof_value,
size_t old_capacity, bool was_soo, size_t old_capacity, bool was_soo,
HashtablezInfoHandle forced_infoz, HashtablezInfoHandle forced_infoz,
CommonFields& c) { CommonFields& c) {
...@@ -1794,12 +1795,12 @@ HashtablezInfoHandle SampleHashtablezInfo(size_t sizeof_slot, ...@@ -1794,12 +1795,12 @@ HashtablezInfoHandle SampleHashtablezInfo(size_t sizeof_slot,
// In SOO, we sample on the first insertion so if this is an empty SOO case // In SOO, we sample on the first insertion so if this is an empty SOO case
// (e.g. when reserve is called), then we still need to sample. // (e.g. when reserve is called), then we still need to sample.
if (kSooEnabled && was_soo && c.size() == 0) { if (kSooEnabled && was_soo && c.size() == 0) {
return Sample(sizeof_slot, SooCapacity()); return Sample(sizeof_slot, sizeof_key, sizeof_value, SooCapacity());
} }
// For non-SOO cases, we sample whenever the capacity is increasing from zero // For non-SOO cases, we sample whenever the capacity is increasing from zero
// to non-zero. // to non-zero.
if (!kSooEnabled && old_capacity == 0) { if (!kSooEnabled && old_capacity == 0) {
return Sample(sizeof_slot, 0); return Sample(sizeof_slot, sizeof_key, sizeof_value, 0);
} }
return c.infoz(); return c.infoz();
} }
...@@ -1902,12 +1903,15 @@ class HashSetResizeHelper { ...@@ -1902,12 +1903,15 @@ class HashSetResizeHelper {
template <typename Alloc, size_t SizeOfSlot, bool TransferUsesMemcpy, template <typename Alloc, size_t SizeOfSlot, bool TransferUsesMemcpy,
bool SooEnabled, size_t AlignOfSlot> bool SooEnabled, size_t AlignOfSlot>
ABSL_ATTRIBUTE_NOINLINE bool InitializeSlots(CommonFields& c, Alloc alloc, ABSL_ATTRIBUTE_NOINLINE bool InitializeSlots(CommonFields& c, Alloc alloc,
ctrl_t soo_slot_h2) { ctrl_t soo_slot_h2,
size_t key_size,
size_t value_size) {
assert(c.capacity()); assert(c.capacity());
HashtablezInfoHandle infoz = HashtablezInfoHandle infoz =
ShouldSampleHashtablezInfo<Alloc>() ShouldSampleHashtablezInfo<Alloc>()
? SampleHashtablezInfo<SooEnabled>(SizeOfSlot, old_capacity_, ? SampleHashtablezInfo<SooEnabled>(SizeOfSlot, key_size, value_size,
was_soo_, forced_infoz_, c) old_capacity_, was_soo_,
forced_infoz_, c)
: HashtablezInfoHandle{}; : HashtablezInfoHandle{};
const bool has_infoz = infoz.IsSampled(); const bool has_infoz = infoz.IsSampled();
...@@ -3362,7 +3366,8 @@ class raw_hash_set { ...@@ -3362,7 +3366,8 @@ class raw_hash_set {
inline HashtablezInfoHandle try_sample_soo() { inline HashtablezInfoHandle try_sample_soo() {
assert(is_soo()); assert(is_soo());
if (!ShouldSampleHashtablezInfo<CharAlloc>()) return HashtablezInfoHandle{}; if (!ShouldSampleHashtablezInfo<CharAlloc>()) return HashtablezInfoHandle{};
return Sample(sizeof(slot_type), SooCapacity()); return Sample(sizeof(slot_type), sizeof(key_type), sizeof(value_type),
SooCapacity());
} }
inline void destroy_slots() { inline void destroy_slots() {
...@@ -3458,7 +3463,8 @@ class raw_hash_set { ...@@ -3458,7 +3463,8 @@ class raw_hash_set {
resize_helper.InitializeSlots<CharAlloc, sizeof(slot_type), resize_helper.InitializeSlots<CharAlloc, sizeof(slot_type),
PolicyTraits::transfer_uses_memcpy(), PolicyTraits::transfer_uses_memcpy(),
SooEnabled(), alignof(slot_type)>( SooEnabled(), alignof(slot_type)>(
common(), CharAlloc(alloc_ref()), soo_slot_h2); common(), CharAlloc(alloc_ref()), soo_slot_h2, sizeof(key_type),
sizeof(value_type));
// In the SooEnabled() case, capacity is never 0 so we don't check. // In the SooEnabled() case, capacity is never 0 so we don't check.
if (!SooEnabled() && resize_helper.old_capacity() == 0) { if (!SooEnabled() && resize_helper.old_capacity() == 0) {
......
...@@ -2571,6 +2571,8 @@ TYPED_TEST_P(RawHashSamplerTest, Sample) { ...@@ -2571,6 +2571,8 @@ TYPED_TEST_P(RawHashSamplerTest, Sample) {
std::memory_order_relaxed)]++; std::memory_order_relaxed)]++;
reservations[info.max_reserve.load(std::memory_order_relaxed)]++; reservations[info.max_reserve.load(std::memory_order_relaxed)]++;
EXPECT_EQ(info.inline_element_size, sizeof(typename TypeParam::value_type)); EXPECT_EQ(info.inline_element_size, sizeof(typename TypeParam::value_type));
EXPECT_EQ(info.key_size, sizeof(typename TypeParam::key_type));
EXPECT_EQ(info.value_size, sizeof(typename TypeParam::value_type));
if (soo_enabled) { if (soo_enabled) {
EXPECT_EQ(info.soo_capacity, SooCapacity()); EXPECT_EQ(info.soo_capacity, SooCapacity());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment