Skip to content

Commit

Permalink
Add additional iteration order tests with reservation.
Browse files Browse the repository at this point in the history
Reserved tables have a different way of iteration randomization compared to gradually resized tables (at least for small tables).

PiperOrigin-RevId: 702848840
Change-Id: I7fc0ef7d828de974dd4b09b24d41a7aa0845f83f
  • Loading branch information
goldvitaly authored and copybara-github committed Dec 4, 2024
1 parent 7316f56 commit 7d96b2e
Showing 1 changed file with 87 additions and 70 deletions.
157 changes: 87 additions & 70 deletions absl/container/internal/raw_hash_set_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2435,8 +2435,9 @@ TYPED_TEST(SooTest, HintInsert) {
}

template <typename T>
T MakeSimpleTable(size_t size) {
T MakeSimpleTable(size_t size, bool do_reserve) {
T t;
if (do_reserve) t.reserve(size);
while (t.size() < size) t.insert(t.size());
return t;
}
Expand All @@ -2455,64 +2456,76 @@ std::vector<int> OrderOfIteration(const T& t) {
// We also need to keep the old tables around to avoid getting the same memory
// blocks over and over.
TYPED_TEST(SooTest, IterationOrderChangesByInstance) {
for (size_t size : {2, 6, 12, 20}) {
const auto reference_table = MakeSimpleTable<TypeParam>(size);
const auto reference = OrderOfIteration(reference_table);

std::vector<TypeParam> tables;
bool found_difference = false;
for (int i = 0; !found_difference && i < 5000; ++i) {
tables.push_back(MakeSimpleTable<TypeParam>(size));
found_difference = OrderOfIteration(tables.back()) != reference;
}
if (!found_difference) {
FAIL()
<< "Iteration order remained the same across many attempts with size "
<< size;
for (bool do_reserve : {false, true}) {
for (size_t size : {2u, 6u, 12u, 20u}) {
SCOPED_TRACE(absl::StrCat("size: ", size, " do_reserve: ", do_reserve));
const auto reference_table = MakeSimpleTable<TypeParam>(size, do_reserve);
const auto reference = OrderOfIteration(reference_table);

std::vector<TypeParam> tables;
bool found_difference = false;
for (int i = 0; !found_difference && i < 5000; ++i) {
tables.push_back(MakeSimpleTable<TypeParam>(size, do_reserve));
found_difference = OrderOfIteration(tables.back()) != reference;
}
if (!found_difference) {
FAIL() << "Iteration order remained the same across many attempts with "
"size "
<< size;
}
}
}
}

TYPED_TEST(SooTest, RelativeIterationOrderChangesByInstance) {
for (size_t size : {size_t{3}, size_t{4}, size_t{5}}) {
std::set<std::pair<int64_t, int64_t>> relative_orders;
auto str = [&] {
std::string out;
for (auto p : relative_orders) {
absl::StrAppend(&out, "{", p.first, ",", p.second, "}", "|");
}
return out;
};
const size_t expected_num_orders = size * (size - 1);

std::vector<std::vector<TypeParam>> tables;
for (int i = 0; relative_orders.size() < expected_num_orders; ++i) {
static constexpr int kBatchSize = 100;
ASSERT_LE(i * kBatchSize, 500)
<< "Relative iteration order remained the same across "
"many attempts with size "
<< size << ". Found " << relative_orders.size() << " out of expected "
<< expected_num_orders << " orders found " << str();

std::vector<TypeParam> batch(kBatchSize);
// Insert into the tables one by one in order to avoid reusing the same
// memory that was freed by the previous table resize.
for (size_t value = 0; value < size; ++value) {
for (auto& table : batch) {
table.insert(value);
for (bool do_reserve : {false, true}) {
for (size_t size : {2u, 3u, 4u, 5u}) {
SCOPED_TRACE(absl::StrCat("size: ", size, " do_reserve: ", do_reserve));
std::set<std::pair<int64_t, int64_t>> relative_orders;
auto str = [&] {
std::string out;
for (auto p : relative_orders) {
absl::StrAppend(&out, "{", p.first, ",", p.second, "}", "|");
}
return out;
};
const size_t expected_num_orders = size * (size - 1);

std::vector<std::vector<TypeParam>> tables;
for (int i = 0; relative_orders.size() < expected_num_orders; ++i) {
static constexpr int kBatchSize = 100;
ASSERT_LE(i * kBatchSize, 500)
<< "Relative iteration order remained the same across "
"many attempts with size "
<< size << ". Found " << relative_orders.size()
<< " out of expected " << expected_num_orders << " orders found "
<< str();

std::vector<TypeParam> batch(kBatchSize);
if (do_reserve) {
for (auto& table : batch) {
table.reserve(size);
}
}
// Insert into the tables one by one in order to avoid reusing the same
// memory that was freed by the previous table resize.
for (size_t value = 0; value < size; ++value) {
for (auto& table : batch) {
table.insert(value);
}
}
}

for (const auto& table : batch) {
auto order = OrderOfIteration(table);
for (auto it = order.begin(); it != order.end(); ++it) {
for (auto it2 = std::next(it); it2 != order.end(); ++it2) {
relative_orders.emplace(*it, *it2);
for (const auto& table : batch) {
auto order = OrderOfIteration(table);
for (auto it = order.begin(); it != order.end(); ++it) {
for (auto it2 = std::next(it); it2 != order.end(); ++it2) {
relative_orders.emplace(*it, *it2);
}
}
}
}

tables.push_back(std::move(batch));
tables.push_back(std::move(batch));
}
}
}
}
Expand All @@ -2521,29 +2534,33 @@ TYPED_TEST(SooTest, IterationOrderChangesOnRehash) {
// We test different sizes with many small numbers, because small table
// resize has a different codepath.
// Note: iteration order for size() <= 1 is always the same.
for (size_t size : std::vector<size_t>{2, 3, 6, 7, 12, 15, 20, 50}) {
for (size_t rehash_size : {
size_t{0}, // Force rehash is guaranteed.
size * 10 // Rehash to the larger capacity is guaranteed.
}) {
std::vector<TypeParam> garbage;
bool ok = false;
for (int i = 0; i < 5000; ++i) {
auto t = MakeSimpleTable<TypeParam>(size);
const auto reference = OrderOfIteration(t);
// Force rehash.
t.rehash(rehash_size);
auto trial = OrderOfIteration(t);
if (trial != reference) {
// We are done.
ok = true;
break;
for (bool do_reserve : {false, true}) {
for (size_t size : {2u, 3u, 6u, 7u, 12u, 15u, 20u, 50u}) {
for (size_t rehash_size : {
size_t{0}, // Force rehash is guaranteed.
size * 10 // Rehash to the larger capacity is guaranteed.
}) {
SCOPED_TRACE(absl::StrCat("size: ", size, " rehash_size: ", rehash_size,
" do_reserve: ", do_reserve));
std::vector<TypeParam> garbage;
bool ok = false;
for (int i = 0; i < 5000; ++i) {
auto t = MakeSimpleTable<TypeParam>(size, do_reserve);
const auto reference = OrderOfIteration(t);
// Force rehash.
t.rehash(rehash_size);
auto trial = OrderOfIteration(t);
if (trial != reference) {
// We are done.
ok = true;
break;
}
garbage.push_back(std::move(t));
}
garbage.push_back(std::move(t));
EXPECT_TRUE(ok)
<< "Iteration order remained the same across many attempts " << size
<< "->" << rehash_size << ".";
}
EXPECT_TRUE(ok)
<< "Iteration order remained the same across many attempts " << size
<< "->" << rehash_size << ".";
}
}
}
Expand Down

0 comments on commit 7d96b2e

Please sign in to comment.