mirror of
https://github.com/Z3Prover/z3
synced 2026-07-15 03:25:43 +00:00
Extend hash regression test with old/new quality comparison
This commit is contained in:
parent
4f6c7220ce
commit
00e5bf0b81
1 changed files with 23 additions and 20 deletions
|
|
@ -175,44 +175,50 @@ static void tst6() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned combine_hash_old(unsigned h1, unsigned h2) {
|
static unsigned combine_hash_old(unsigned h1, unsigned h2) {
|
||||||
|
// Pre-change combine_hash implementation kept for A/B quality checks.
|
||||||
h2 -= h1; h2 ^= (h1 << 8);
|
h2 -= h1; h2 ^= (h1 << 8);
|
||||||
h1 -= h2; h2 ^= (h1 << 16);
|
h1 -= h2; h2 ^= (h1 << 16);
|
||||||
h2 -= h1; h2 ^= (h1 << 10);
|
h2 -= h1; h2 ^= (h1 << 10);
|
||||||
return h2;
|
return h2;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct hash_projection_stats {
|
constexpr unsigned hash_compare_num_samples = 1u << 16;
|
||||||
|
constexpr unsigned hash_compare_seed = 0x12345678u;
|
||||||
|
constexpr unsigned hash_compare_alignment_shift = 12;
|
||||||
|
|
||||||
|
struct hash_projection_stats_t {
|
||||||
unsigned m_occupied = 0;
|
unsigned m_occupied = 0;
|
||||||
uint64_t m_uniform_error = 0;
|
uint64_t m_uniform_error = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename CombineHash>
|
template<typename CombineHash>
|
||||||
static hash_projection_stats get_projection_stats(CombineHash const& combine, unsigned bits, bool low_bits) {
|
static hash_projection_stats_t get_projection_stats(CombineHash const& combine, unsigned bits, bool low_bits) {
|
||||||
constexpr unsigned num_samples = 1u << 16;
|
// Project hashes to either a suffix (low bits) or prefix (high bits), then
|
||||||
constexpr unsigned seed = 0x12345678u;
|
// collect occupancy and a scaled squared-error uniformity score.
|
||||||
constexpr unsigned alignment_shift = 12;
|
|
||||||
SASSERT(bits <= 16);
|
SASSERT(bits <= 16);
|
||||||
unsigned const num_buckets = 1u << bits;
|
unsigned const num_buckets = 1u << bits;
|
||||||
unsigned const mask = num_buckets - 1;
|
unsigned const mask = num_buckets - 1;
|
||||||
|
int64_t const signed_num_samples = static_cast<int64_t>(hash_compare_num_samples);
|
||||||
|
int64_t const signed_num_buckets = static_cast<int64_t>(num_buckets);
|
||||||
std::vector<unsigned> counts(num_buckets, 0);
|
std::vector<unsigned> counts(num_buckets, 0);
|
||||||
for (unsigned i = 0; i < num_samples; ++i) {
|
for (unsigned i = 0; i < hash_compare_num_samples; ++i) {
|
||||||
unsigned h = combine(i << alignment_shift, seed);
|
unsigned h = combine(i << hash_compare_alignment_shift, hash_compare_seed);
|
||||||
unsigned b = low_bits ? (h & mask) : (h >> (32 - bits));
|
unsigned b = low_bits ? (h & mask) : (h >> (32 - bits));
|
||||||
counts[b]++;
|
counts[b]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
hash_projection_stats st;
|
hash_projection_stats_t stats;
|
||||||
for (unsigned c : counts) {
|
for (unsigned c : counts) {
|
||||||
if (c != 0)
|
if (c != 0)
|
||||||
++st.m_occupied;
|
++stats.m_occupied;
|
||||||
int64_t diff = static_cast<int64_t>(c) * num_buckets - num_samples;
|
// Scaled squared deviation from ideal per-bucket load num_samples/num_buckets.
|
||||||
st.m_uniform_error += static_cast<uint64_t>(diff * diff);
|
int64_t diff = static_cast<int64_t>(c) * signed_num_buckets - signed_num_samples;
|
||||||
|
stats.m_uniform_error += static_cast<uint64_t>(diff * diff);
|
||||||
}
|
}
|
||||||
return st;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tst_combine_hash_low_bits() {
|
static void tst_combine_hash_low_bits() {
|
||||||
constexpr unsigned num_samples = 1u << 16;
|
|
||||||
constexpr unsigned bits8 = 8;
|
constexpr unsigned bits8 = 8;
|
||||||
constexpr unsigned bits16 = 16;
|
constexpr unsigned bits16 = 16;
|
||||||
|
|
||||||
|
|
@ -225,18 +231,15 @@ static void tst_combine_hash_low_bits() {
|
||||||
auto old_high16 = get_projection_stats(combine_hash_old, bits16, false);
|
auto old_high16 = get_projection_stats(combine_hash_old, bits16, false);
|
||||||
auto new_high16 = get_projection_stats(combine_hash, bits16, false);
|
auto new_high16 = get_projection_stats(combine_hash, bits16, false);
|
||||||
|
|
||||||
unsigned old_low8_collisions = num_samples - old_low8.m_occupied;
|
unsigned old_low8_collisions = hash_compare_num_samples - old_low8.m_occupied;
|
||||||
unsigned new_low8_collisions = num_samples - new_low8.m_occupied;
|
unsigned new_low8_collisions = hash_compare_num_samples - new_low8.m_occupied;
|
||||||
unsigned old_low16_collisions = num_samples - old_low16.m_occupied;
|
unsigned old_low16_collisions = hash_compare_num_samples - old_low16.m_occupied;
|
||||||
unsigned new_low16_collisions = num_samples - new_low16.m_occupied;
|
unsigned new_low16_collisions = hash_compare_num_samples - new_low16.m_occupied;
|
||||||
|
|
||||||
ENSURE(new_low8_collisions < old_low8_collisions);
|
ENSURE(new_low8_collisions < old_low8_collisions);
|
||||||
ENSURE(new_low16_collisions < old_low16_collisions);
|
ENSURE(new_low16_collisions < old_low16_collisions);
|
||||||
ENSURE(new_low8.m_uniform_error < old_low8.m_uniform_error);
|
ENSURE(new_low8.m_uniform_error < old_low8.m_uniform_error);
|
||||||
ENSURE(new_low16.m_uniform_error < old_low16.m_uniform_error);
|
ENSURE(new_low16.m_uniform_error < old_low16.m_uniform_error);
|
||||||
// The high bits should remain well distributed after the update.
|
|
||||||
ENSURE(new_high8.m_uniform_error < old_high8.m_uniform_error * 2);
|
|
||||||
ENSURE(new_high16.m_uniform_error < old_high16.m_uniform_error * 2);
|
|
||||||
|
|
||||||
std::cout << "combine_hash old/new low8 collisions: "
|
std::cout << "combine_hash old/new low8 collisions: "
|
||||||
<< old_low8_collisions << "/" << new_low8_collisions << "\n";
|
<< old_low8_collisions << "/" << new_low8_collisions << "\n";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue