Always "mix" hash values in case poor hash values are provided.

This commit is contained in:
greg
2019-04-05 21:12:24 -04:00
parent 6a720b4d0c
commit 7cc2644fba
3 changed files with 187 additions and 108 deletions
+69 -5
View File
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// define this so that the "IterationOrderChanges" tests pass
#define PHMAP_NON_DETERMINISTIC 1
#include "parallel_hashmap/phmap.h"
//#include "container_memory.h"
//#include "hash_function_defaults.h"
@@ -349,6 +352,25 @@ struct IntTable
using Base::Base;
};
template <typename T>
struct CustomAlloc : std::allocator<T> {
CustomAlloc() {}
template <typename U>
CustomAlloc(const CustomAlloc<U>& other) {}
template<class U> struct rebind {
using other = CustomAlloc<U>;
};
};
struct CustomAllocIntTable
: raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
std::equal_to<int64_t>, CustomAlloc<int64_t>> {
using Base = typename CustomAllocIntTable::raw_hash_set;
using Base::Base;
};
struct BadFastHash {
template <class T>
size_t operator()(const T&) const {
@@ -855,6 +877,25 @@ TEST(Table, Erase) {
EXPECT_TRUE(t.find(0) == t.end());
}
TEST(Table, EraseMaintainsValidIterator) {
IntTable t;
const int kNumElements = 100;
for (int i = 0; i < kNumElements; i ++) {
EXPECT_TRUE(t.emplace(i).second);
}
EXPECT_EQ(t.size(), kNumElements);
int num_erase_calls = 0;
auto it = t.begin();
while (it != t.end()) {
t.erase(it++);
num_erase_calls++;
}
EXPECT_TRUE(t.empty());
EXPECT_EQ(num_erase_calls, kNumElements);
}
// Collect N bad keys by following algorithm:
// 1. Create an empty table and reserve it to 2 * N.
// 2. Insert N random elements.
@@ -1581,7 +1622,7 @@ TEST(Table, HeterogeneousLookup) {
size_t operator()(int64_t i) const { return i; }
size_t operator()(double i) const {
ADD_FAILURE();
return i;
return (size_t)i;
}
};
struct Eq {
@@ -1603,7 +1644,7 @@ TEST(Table, HeterogeneousLookup) {
struct THash {
using is_transparent = void;
size_t operator()(int64_t i) const { return i; }
size_t operator()(double i) const { return i; }
size_t operator()(double i) const { return (size_t)i; }
};
struct TEq {
using is_transparent = void;
@@ -1615,7 +1656,7 @@ TEST(Table, HeterogeneousLookup) {
raw_hash_set<IntPolicy, Hash, Eq, Alloc<int64_t>> s{0, 1, 2};
// It will convert to int64_t before the query.
EXPECT_EQ(1, *s.find(double{1.1}));
EXPECT_EQ(1, *s.find((int)double{1.1}));
raw_hash_set<IntPolicy, THash, TEq, Alloc<int64_t>> ts{0, 1, 2};
// It will try to use the double, and fail to find the object.
@@ -1778,6 +1819,7 @@ std::vector<int> OrderOfIteration(const IntTable& t) {
// we are touching different memory pages to cause the ordering to change.
// We also need to keep the old tables around to avoid getting the same memory
// blocks over and over.
// not randomizing in phmap
TEST(Table, IterationOrderChangesByInstance) {
for (size_t size : {2, 6, 12, 20}) {
const auto reference_table = MakeSimpleTable(size);
@@ -1785,7 +1827,7 @@ TEST(Table, IterationOrderChangesByInstance) {
std::vector<IntTable> tables;
bool found_difference = false;
for (int i = 0; !found_difference && i < 500; ++i) {
for (int i = 0; !found_difference && i < 5000; ++i) {
tables.push_back(MakeSimpleTable(size));
found_difference = OrderOfIteration(tables.back()) != reference;
}
@@ -1797,9 +1839,10 @@ TEST(Table, IterationOrderChangesByInstance) {
}
}
// not randomizing in phmap
TEST(Table, IterationOrderChangesOnRehash) {
std::vector<IntTable> garbage;
for (int i = 0; i < 500; ++i) {
for (int i = 0; i < 5000; ++i) {
auto t = MakeSimpleTable(20);
const auto reference = OrderOfIteration(t);
// Force rehash to the same size.
@@ -1870,6 +1913,27 @@ TEST(RawHashSamplerTest, DISABLED_Sample) {
0.01, 0.005);
}
TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) {
// Enable the feature even if the prod default is off.
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(100);
auto& sampler = HashtablezSampler::Global();
size_t start_size = 0;
start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; });
std::vector<CustomAllocIntTable> tables;
for (int i = 0; i < 1000000; ++i) {
tables.emplace_back();
tables.back().insert(1);
}
size_t end_size = 0;
end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; });
EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()),
0.00, 0.001);
}
#ifdef ADDRESS_SANITIZER
TEST(Sanitizer, PoisoningUnused) {
IntTable t;