pull/13171/head
Joshua Haberman 3 years ago
parent 738b1d6d9c
commit 94ce6a35c2
  1. 2
      tests/BUILD
  2. 302
      tests/test_table.cc

@ -131,10 +131,10 @@ cc_test(
srcs = ["test_table.cc"],
copts = UPB_DEFAULT_CPPOPTS,
deps = [
":upb_test",
"//:port",
"//:table",
"//:upb",
"@com_google_googletest//:gtest_main",
],
)

@ -38,7 +38,7 @@
#include <unordered_map>
#include <vector>
#include "tests/upb_test.h"
#include "gtest/gtest.h"
#include "upb/table_internal.h"
#include "upb/upb.hpp"
@ -365,46 +365,63 @@ double get_usertime() {
return usage.ru_utime.tv_sec + (usage.ru_utime.tv_usec / 1000000.0);
}
/* num_entries must be a power of 2. */
void test_strtable(const vector<std::string>& keys, uint32_t num_to_insert) {
TEST(Table, StringTable) {
vector<std::string> keys;
keys.push_back("google.protobuf.FileDescriptorSet");
keys.push_back("google.protobuf.FileDescriptorProto");
keys.push_back("google.protobuf.DescriptorProto");
keys.push_back("google.protobuf.DescriptorProto.ExtensionRange");
keys.push_back("google.protobuf.FieldDescriptorProto");
keys.push_back("google.protobuf.EnumDescriptorProto");
keys.push_back("google.protobuf.EnumValueDescriptorProto");
keys.push_back("google.protobuf.ServiceDescriptorProto");
keys.push_back("google.protobuf.MethodDescriptorProto");
keys.push_back("google.protobuf.FileOptions");
keys.push_back("google.protobuf.MessageOptions");
keys.push_back("google.protobuf.FieldOptions");
keys.push_back("google.protobuf.EnumOptions");
keys.push_back("google.protobuf.EnumValueOptions");
keys.push_back("google.protobuf.ServiceOptions");
keys.push_back("google.protobuf.MethodOptions");
keys.push_back("google.protobuf.UninterpretedOption");
keys.push_back("google.protobuf.UninterpretedOption.NamePart");
/* Initialize structures. */
std::map<std::string, int32_t> m;
typedef upb::TypedStrTable<int32_t> Table;
Table table;
std::set<std::string> all;
for (size_t i = 0; i < num_to_insert; i++) {
const std::string& key = keys[i];
for (const auto& key : keys) {
all.insert(key);
table.Insert(key, key[0]);
m[key] = key[0];
}
/* Test correctness. */
for (uint32_t i = 0; i < keys.size(); i++) {
const std::string& key = keys[i];
for (const auto& key : keys) {
std::pair<bool, int32_t> found = table.Lookup(key);
if (m.find(key) != m.end()) { /* Assume map implementation is correct. */
ASSERT(found.first);
ASSERT(found.second == key[0]);
ASSERT(m[key] == key[0]);
EXPECT_TRUE(found.first);
EXPECT_EQ(found.second, key[0]);
EXPECT_EQ(m[key], key[0]);
} else {
ASSERT(!found.first);
EXPECT_FALSE(found.first);
}
}
for (Table::iterator it = table.begin(); it != table.end(); ++it) {
std::set<std::string>::iterator i = all.find((*it).first);
ASSERT(i != all.end());
EXPECT_NE(i, all.end());
all.erase(i);
}
ASSERT(all.empty());
EXPECT_TRUE(all.empty());
// Test iteration with resizes.
for (int i = 0; i < 10; i++) {
for (Table::iterator it = table.begin(); it != table.end(); ++it) {
// Even if we invalidate the iterator it should only return real elements.
ASSERT((*it).second == m[(*it).first]);
EXPECT_EQ((*it).second, m[(*it).first]);
// Force a resize even though the size isn't changing.
// Also forces the table size to grow so some new buckets end up empty.
@ -416,16 +433,34 @@ void test_strtable(const vector<std::string>& keys, uint32_t num_to_insert) {
}
}
/* num_entries must be a power of 2. */
void test_inttable(int32_t* keys, uint16_t num_entries, const char* desc) {
class IntTableTest : public testing::TestWithParam<int> {
void SetUp() override {
if (GetParam() > 0) {
for (int i = 0; i < GetParam(); i++) {
keys_.push_back(i + 1);
}
} else {
for (int32_t i = 0; i < 64; i++) {
if (i < 32)
keys_.push_back(i + 1);
else
keys_.push_back(10101 + i);
}
}
}
protected:
std::vector<int32_t> keys_;
};
TEST_P(IntTableTest, TestIntTable) {
/* Initialize structures. */
typedef upb::TypedIntTable<uint32_t> Table;
Table table;
uint32_t largest_key = 0;
std::map<uint32_t, uint32_t> m;
std::unordered_map<uint32_t, uint32_t> hm;
for (size_t i = 0; i < num_entries; i++) {
int32_t key = keys[i];
for (const auto& key: keys_) {
largest_key = UPB_MAX((int32_t)largest_key, key);
table.Insert(key, key * 2);
m[key] = key * 2;
@ -436,35 +471,35 @@ void test_inttable(int32_t* keys, uint16_t num_entries, const char* desc) {
for (uint32_t i = 0; i <= largest_key; i++) {
std::pair<bool, uint32_t> found = table.Lookup(i);
if (m.find(i) != m.end()) { /* Assume map implementation is correct. */
ASSERT(found.first);
ASSERT(found.second == i * 2);
ASSERT(m[i] == i * 2);
ASSERT(hm[i] == i * 2);
EXPECT_TRUE(found.first);
EXPECT_EQ(found.second, i * 2);
EXPECT_EQ(m[i], i * 2);
EXPECT_EQ(hm[i], i * 2);
} else {
ASSERT(!found.first);
EXPECT_FALSE(found.first);
}
}
for (uint16_t i = 0; i < num_entries; i += 2) {
std::pair<bool, uint32_t> found = table.Remove(keys[i]);
ASSERT(found.first == (m.erase(keys[i]) == 1));
if (found.first) ASSERT(found.second == (uint32_t)keys[i] * 2);
hm.erase(keys[i]);
m.erase(keys[i]);
for (size_t i = 0; i < keys_.size(); i += 2) {
std::pair<bool, uint32_t> found = table.Remove(keys_[i]);
EXPECT_EQ(found.first, m.erase(keys_[i]) == 1);
if (found.first) EXPECT_EQ(found.second, (uint32_t)keys_[i] * 2);
hm.erase(keys_[i]);
m.erase(keys_[i]);
}
ASSERT(table.count() == hm.size());
EXPECT_EQ(table.count(), hm.size());
/* Test correctness. */
for (uint32_t i = 0; i <= largest_key; i++) {
std::pair<bool, uint32_t> found = table.Lookup(i);
if (m.find(i) != m.end()) { /* Assume map implementation is correct. */
ASSERT(found.first);
ASSERT(found.second == i * 2);
ASSERT(m[i] == i * 2);
ASSERT(hm[i] == i * 2);
EXPECT_TRUE(found.first);
EXPECT_EQ(found.second, i * 2);
EXPECT_EQ(m[i], i * 2);
EXPECT_EQ(hm[i], i * 2);
} else {
ASSERT(!found.first);
EXPECT_FALSE(found.first);
}
}
@ -472,11 +507,11 @@ void test_inttable(int32_t* keys, uint16_t num_entries, const char* desc) {
for (uint32_t i = 0; i <= largest_key; i++) {
bool replaced = table.Replace(i, i * 3);
if (m.find(i) != m.end()) { /* Assume map implementation is correct. */
ASSERT(replaced);
EXPECT_TRUE(replaced);
m[i] = i * 3;
hm[i] = i * 3;
} else {
ASSERT(!replaced);
EXPECT_FALSE(replaced);
}
}
@ -485,123 +520,24 @@ void test_inttable(int32_t* keys, uint16_t num_entries, const char* desc) {
for (uint32_t i = 0; i <= largest_key; i++) {
std::pair<bool, uint32_t> found = table.Lookup(i);
if (m.find(i) != m.end()) { /* Assume map implementation is correct. */
ASSERT(found.first);
ASSERT(found.second == i * 3);
ASSERT(m[i] == i * 3);
ASSERT(hm[i] == i * 3);
EXPECT_TRUE(found.first);
EXPECT_EQ(found.second, i * 3);
EXPECT_EQ(m[i], i * 3);
EXPECT_EQ(hm[i], i * 3);
} else {
ASSERT(!found.first);
}
}
if (!benchmark) {
return;
}
printf("%s\n", desc);
/* Test performance. We only test lookups for keys that are known to exist. */
uint16_t* rand_order = new uint16_t[num_entries];
for (uint16_t i = 0; i < num_entries; i++) {
rand_order[i] = i;
}
for (uint16_t i = num_entries - 1; i >= 1; i--) {
uint16_t rand_i = (random() / (double)RAND_MAX) * i;
ASSERT(rand_i <= i);
uint16_t tmp = rand_order[rand_i];
rand_order[rand_i] = rand_order[i];
rand_order[i] = tmp;
}
uintptr_t x = 0;
const int mask = num_entries - 1;
int time_mask = 0xffff;
printf("upb_inttable(seq): ");
fflush(stdout);
double before = get_usertime();
unsigned int i;
#define MAYBE_BREAK \
if ((i & time_mask) == 0 && (get_usertime() - before) > CPU_TIME_PER_TEST) \
break;
for (i = 0; true; i++) {
MAYBE_BREAK;
int32_t key = keys[i & mask];
upb_value v;
bool ok = upb_inttable_lookup(&table.table_.table_, key, &v);
x += (uintptr_t)ok;
}
double total = get_usertime() - before;
printf("%ld/s\n", (long)(i / total));
double upb_seq_i = i / 100; // For later percentage calcuation.
printf("upb_inttable(rand): ");
fflush(stdout);
before = get_usertime();
for (i = 0; true; i++) {
MAYBE_BREAK;
int32_t key = keys[rand_order[i & mask]];
upb_value v;
bool ok = upb_inttable_lookup(&table.table_.table_, key, &v);
x += (uintptr_t)ok;
}
total = get_usertime() - before;
printf("%ld/s\n", (long)(i / total));
double upb_rand_i = i / 100; // For later percentage calculation.
printf("std::map<int32_t, int32_t>(seq): ");
fflush(stdout);
before = get_usertime();
for (i = 0; true; i++) {
MAYBE_BREAK;
int32_t key = keys[i & mask];
x += m[key];
}
total = get_usertime() - before;
printf("%ld/s (%0.1f%% of upb)\n", (long)(i / total), i / upb_seq_i);
printf("std::map<int32_t, int32_t>(rand): ");
fflush(stdout);
before = get_usertime();
for (i = 0; true; i++) {
MAYBE_BREAK;
int32_t key = keys[rand_order[i & mask]];
x += m[key];
}
total = get_usertime() - before;
printf("%ld/s (%0.1f%% of upb)\n", (long)(i / total), i / upb_rand_i);
printf("std::unordered_map<uint32_t, uint32_t>(seq): ");
fflush(stdout);
before = get_usertime();
for (i = 0; true; i++) {
MAYBE_BREAK;
int32_t key = keys[rand_order[i & mask]];
x += hm[key];
}
total = get_usertime() - before;
printf("%ld/s (%0.1f%% of upb)\n", (long)(i / total), i / upb_seq_i);
printf("std::unordered_map<uint32_t, uint32_t>(rand): ");
fflush(stdout);
before = get_usertime();
for (i = 0; true; i++) {
MAYBE_BREAK;
int32_t key = keys[rand_order[i & mask]];
x += hm[key];
}
total = get_usertime() - before;
if (x == INT_MAX) abort();
printf("%ld/s (%0.1f%% of upb)\n\n", (long)(i / total), i / upb_rand_i);
delete[] rand_order;
EXPECT_FALSE(found.first);
}
}
}
INSTANTIATE_TEST_SUITE_P(IntTableParams, IntTableTest,
testing::Values(8, 64, 512, -32));
/*
* This test can't pass right now because the table can't store a value of
* (uint64_t)-1.
*/
void test_int64_max_value() {
TEST(Table, MaxValue) {
/*
typedef upb::TypedIntTable<uint64_t> Table;
Table table;
@ -613,13 +549,7 @@ void test_int64_max_value() {
*/
}
int32_t* get_contiguous_keys(int32_t num) {
int32_t* buf = new int32_t[num];
for (int32_t i = 0; i < num; i++) buf[i] = i;
return buf;
}
void test_delete() {
TEST(Table, Delete) {
upb::Arena arena;
upb_inttable t;
upb_inttable_init(&t, arena.ptr());
@ -634,11 +564,11 @@ void test_delete() {
upb_inttable_iter iter;
for (upb_inttable_begin(&iter, &t); !upb_inttable_done(&iter);
upb_inttable_next(&iter)) {
ASSERT(false);
ASSERT_TRUE(false);
}
}
void test_init() {
TEST(Table, Init) {
for (int i = 0; i < 2048; i++) {
/* Tests that the size calculations in init() (lg2 size for target load)
* work for all expected sizes. */
@ -647,63 +577,3 @@ void test_init() {
upb_strtable_init(&t, i, arena.ptr());
}
}
extern "C" {
int run_tests(int argc, char* argv[]) {
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "benchmark") == 0) benchmark = true;
}
vector<std::string> keys;
keys.push_back("google.protobuf.FileDescriptorSet");
keys.push_back("google.protobuf.FileDescriptorProto");
keys.push_back("google.protobuf.DescriptorProto");
keys.push_back("google.protobuf.DescriptorProto.ExtensionRange");
keys.push_back("google.protobuf.FieldDescriptorProto");
keys.push_back("google.protobuf.EnumDescriptorProto");
keys.push_back("google.protobuf.EnumValueDescriptorProto");
keys.push_back("google.protobuf.ServiceDescriptorProto");
keys.push_back("google.protobuf.MethodDescriptorProto");
keys.push_back("google.protobuf.FileOptions");
keys.push_back("google.protobuf.MessageOptions");
keys.push_back("google.protobuf.FieldOptions");
keys.push_back("google.protobuf.EnumOptions");
keys.push_back("google.protobuf.EnumValueOptions");
keys.push_back("google.protobuf.ServiceOptions");
keys.push_back("google.protobuf.MethodOptions");
keys.push_back("google.protobuf.UninterpretedOption");
keys.push_back("google.protobuf.UninterpretedOption.NamePart");
for (int i = 0; i < 10; i++) {
test_strtable(keys, 18);
}
int32_t* keys1 = get_contiguous_keys(8);
test_inttable(keys1, 8, "Table size: 8, keys: 1-8 ====");
delete[] keys1;
int32_t* keys2 = get_contiguous_keys(64);
test_inttable(keys2, 64, "Table size: 64, keys: 1-64 ====\n");
delete[] keys2;
int32_t* keys3 = get_contiguous_keys(512);
test_inttable(keys3, 512, "Table size: 512, keys: 1-512 ====\n");
delete[] keys3;
int32_t* keys4 = new int32_t[64];
for (int32_t i = 0; i < 64; i++) {
if (i < 32)
keys4[i] = i + 1;
else
keys4[i] = 10101 + i;
}
test_inttable(keys4, 64, "Table size: 64, keys: 1-32 and 10133-10164 ====\n");
delete[] keys4;
test_delete();
test_int64_max_value();
return 0;
}
}

Loading…
Cancel
Save