6d0fe3e45a
..ordered-hash-table.h. Bug: v8:8834 Change-Id: Ibb79006beb020d06bc516d9a2dac81015a403728 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1538518 Reviewed-by: Georg Neis <neis@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Commit-Queue: Sigurd Schneider <sigurds@chromium.org> Cr-Commit-Position: refs/heads/master@{#60461}
302 lines
9.7 KiB
C++
302 lines
9.7 KiB
C++
// Copyright 2017 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <stdlib.h>
|
|
#include <sstream>
|
|
#include <utility>
|
|
|
|
#include "src/objects-inl.h"
|
|
#include "src/objects.h"
|
|
#include "src/objects/ordered-hash-table.h"
|
|
#include "src/third_party/siphash/halfsiphash.h"
|
|
#include "src/utils.h"
|
|
#include "src/v8.h"
|
|
|
|
#include "test/cctest/cctest.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
int AddToSetAndGetHash(Isolate* isolate, Handle<JSObject> obj,
|
|
bool has_fast_properties) {
|
|
CHECK_EQ(has_fast_properties, obj->HasFastProperties());
|
|
CHECK_EQ(ReadOnlyRoots(isolate).undefined_value(), obj->GetHash());
|
|
Handle<OrderedHashSet> set = isolate->factory()->NewOrderedHashSet();
|
|
OrderedHashSet::Add(isolate, set, obj);
|
|
CHECK_EQ(has_fast_properties, obj->HasFastProperties());
|
|
return Smi::ToInt(obj->GetHash());
|
|
}
|
|
|
|
void CheckFastObject(Handle<JSObject> obj, int hash) {
|
|
CHECK(obj->HasFastProperties());
|
|
CHECK(obj->raw_properties_or_hash()->IsPropertyArray());
|
|
CHECK_EQ(Smi::FromInt(hash), obj->GetHash());
|
|
CHECK_EQ(hash, obj->property_array()->Hash());
|
|
}
|
|
|
|
void CheckDictionaryObject(Handle<JSObject> obj, int hash) {
|
|
CHECK(!obj->HasFastProperties());
|
|
CHECK(obj->raw_properties_or_hash()->IsDictionary());
|
|
CHECK_EQ(Smi::FromInt(hash), obj->GetHash());
|
|
CHECK_EQ(hash, obj->property_dictionary()->Hash());
|
|
}
|
|
|
|
TEST(AddHashCodeToFastObjectWithoutProperties) {
|
|
CcTest::InitializeVM();
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
Handle<JSObject> obj =
|
|
isolate->factory()->NewJSObject(isolate->object_function());
|
|
CHECK(obj->HasFastProperties());
|
|
|
|
int hash = AddToSetAndGetHash(isolate, obj, true);
|
|
CHECK_EQ(Smi::FromInt(hash), obj->raw_properties_or_hash());
|
|
}
|
|
|
|
TEST(AddHashCodeToFastObjectWithInObjectProperties) {
|
|
CcTest::InitializeVM();
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
const char* source = " var x = { a: 1};";
|
|
CompileRun(source);
|
|
|
|
Handle<JSObject> obj = GetGlobal<JSObject>("x");
|
|
CHECK_EQ(ReadOnlyRoots(isolate).empty_fixed_array(),
|
|
obj->raw_properties_or_hash());
|
|
|
|
int hash = AddToSetAndGetHash(isolate, obj, true);
|
|
CHECK_EQ(Smi::FromInt(hash), obj->raw_properties_or_hash());
|
|
}
|
|
|
|
TEST(AddHashCodeToFastObjectWithPropertiesArray) {
|
|
CcTest::InitializeVM();
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
const char* source =
|
|
" var x = {}; "
|
|
" x.a = 1; x.b = 2; x.c = 3; x.d = 4; x.e = 5; ";
|
|
CompileRun(source);
|
|
|
|
Handle<JSObject> obj = GetGlobal<JSObject>("x");
|
|
CHECK(obj->HasFastProperties());
|
|
|
|
int hash = AddToSetAndGetHash(isolate, obj, true);
|
|
CheckFastObject(obj, hash);
|
|
}
|
|
|
|
TEST(AddHashCodeToSlowObject) {
|
|
CcTest::InitializeVM();
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
Handle<JSObject> obj =
|
|
isolate->factory()->NewJSObject(isolate->object_function());
|
|
CHECK(obj->HasFastProperties());
|
|
JSObject::NormalizeProperties(obj, CLEAR_INOBJECT_PROPERTIES, 0,
|
|
"cctest/test-hashcode");
|
|
CHECK(obj->raw_properties_or_hash()->IsDictionary());
|
|
|
|
int hash = AddToSetAndGetHash(isolate, obj, false);
|
|
CheckDictionaryObject(obj, hash);
|
|
}
|
|
|
|
TEST(TransitionFastWithInObjectToFastWithPropertyArray) {
|
|
CcTest::InitializeVM();
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
const char* source =
|
|
" var x = { };"
|
|
" x.a = 1; x.b = 2; x.c = 3; x.d = 4;";
|
|
CompileRun(source);
|
|
|
|
Handle<JSObject> obj = GetGlobal<JSObject>("x");
|
|
CHECK(obj->HasFastProperties());
|
|
|
|
int hash = AddToSetAndGetHash(isolate, obj, true);
|
|
CHECK_EQ(Smi::FromInt(hash), obj->raw_properties_or_hash());
|
|
|
|
int length = obj->property_array()->length();
|
|
CompileRun("x.e = 5;");
|
|
CHECK(obj->property_array()->length() > length);
|
|
CheckFastObject(obj, hash);
|
|
}
|
|
|
|
TEST(TransitionFastWithPropertyArray) {
|
|
CcTest::InitializeVM();
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
const char* source =
|
|
" var x = { };"
|
|
" x.a = 1; x.b = 2; x.c = 3; x.d = 4; x.e = 5; ";
|
|
CompileRun(source);
|
|
|
|
Handle<JSObject> obj = GetGlobal<JSObject>("x");
|
|
CHECK(obj->raw_properties_or_hash()->IsPropertyArray());
|
|
|
|
int hash = AddToSetAndGetHash(isolate, obj, true);
|
|
CHECK_EQ(hash, obj->property_array()->Hash());
|
|
|
|
int length = obj->property_array()->length();
|
|
CompileRun("x.f = 2; x.g = 5; x.h = 2");
|
|
CHECK(obj->property_array()->length() > length);
|
|
CheckFastObject(obj, hash);
|
|
}
|
|
|
|
TEST(TransitionFastWithPropertyArrayToSlow) {
|
|
CcTest::InitializeVM();
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
const char* source =
|
|
" var x = { };"
|
|
" x.a = 1; x.b = 2; x.c = 3; x.d = 4; x.e = 5; ";
|
|
CompileRun(source);
|
|
|
|
Handle<JSObject> obj = GetGlobal<JSObject>("x");
|
|
CHECK(obj->raw_properties_or_hash()->IsPropertyArray());
|
|
|
|
int hash = AddToSetAndGetHash(isolate, obj, true);
|
|
CHECK(obj->raw_properties_or_hash()->IsPropertyArray());
|
|
CHECK_EQ(hash, obj->property_array()->Hash());
|
|
|
|
JSObject::NormalizeProperties(obj, KEEP_INOBJECT_PROPERTIES, 0,
|
|
"cctest/test-hashcode");
|
|
CheckDictionaryObject(obj, hash);
|
|
}
|
|
|
|
TEST(TransitionSlowToSlow) {
|
|
CcTest::InitializeVM();
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
const char* source = " var x = {}; ";
|
|
CompileRun(source);
|
|
|
|
Handle<JSObject> obj = GetGlobal<JSObject>("x");
|
|
JSObject::NormalizeProperties(obj, CLEAR_INOBJECT_PROPERTIES, 0,
|
|
"cctest/test-hashcode");
|
|
CHECK(obj->raw_properties_or_hash()->IsDictionary());
|
|
|
|
int hash = AddToSetAndGetHash(isolate, obj, false);
|
|
CHECK_EQ(hash, obj->property_dictionary()->Hash());
|
|
|
|
int length = obj->property_dictionary()->length();
|
|
CompileRun("for(var i = 0; i < 10; i++) { x['f'+i] = i };");
|
|
CHECK(obj->property_dictionary()->length() > length);
|
|
CheckDictionaryObject(obj, hash);
|
|
}
|
|
|
|
TEST(TransitionSlowToFastWithoutProperties) {
|
|
CcTest::InitializeVM();
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
Handle<JSObject> obj =
|
|
isolate->factory()->NewJSObject(isolate->object_function());
|
|
JSObject::NormalizeProperties(obj, CLEAR_INOBJECT_PROPERTIES, 0,
|
|
"cctest/test-hashcode");
|
|
CHECK(obj->raw_properties_or_hash()->IsDictionary());
|
|
|
|
int hash = AddToSetAndGetHash(isolate, obj, false);
|
|
CHECK_EQ(hash, obj->property_dictionary()->Hash());
|
|
|
|
JSObject::MigrateSlowToFast(obj, 0, "cctest/test-hashcode");
|
|
CHECK_EQ(Smi::FromInt(hash), obj->GetHash());
|
|
}
|
|
|
|
TEST(TransitionSlowToFastWithPropertyArray) {
|
|
CcTest::InitializeVM();
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
const char* source =
|
|
" var x = Object.create(null); "
|
|
" for(var i = 0; i < 10; i++) { x['f'+i] = i }; ";
|
|
CompileRun(source);
|
|
|
|
Handle<JSObject> obj = GetGlobal<JSObject>("x");
|
|
CHECK(obj->raw_properties_or_hash()->IsDictionary());
|
|
|
|
int hash = AddToSetAndGetHash(isolate, obj, false);
|
|
CHECK_EQ(hash, obj->property_dictionary()->Hash());
|
|
|
|
JSObject::MigrateSlowToFast(obj, 0, "cctest/test-hashcode");
|
|
CheckFastObject(obj, hash);
|
|
}
|
|
|
|
namespace {
|
|
|
|
typedef uint32_t (*HashFunction)(uint32_t key, uint64_t seed);
|
|
|
|
void TestIntegerHashQuality(const int samples_log2, int num_buckets_log2,
|
|
uint64_t seed, double max_var,
|
|
HashFunction hash_function) {
|
|
int samples = 1 << samples_log2;
|
|
int num_buckets = 1 << num_buckets_log2;
|
|
int mean = samples / num_buckets;
|
|
int* buckets = new int[num_buckets];
|
|
|
|
for (int i = 0; i < num_buckets; i++) buckets[i] = 0;
|
|
|
|
for (int i = 0; i < samples; i++) {
|
|
uint32_t hash = hash_function(i, seed);
|
|
buckets[hash % num_buckets]++;
|
|
}
|
|
|
|
int sum_deviation = 0;
|
|
for (int i = 0; i < num_buckets; i++) {
|
|
int deviation = abs(buckets[i] - mean);
|
|
sum_deviation += deviation * deviation;
|
|
}
|
|
delete[] buckets;
|
|
|
|
double variation_coefficient = sqrt(sum_deviation * 1.0 / num_buckets) / mean;
|
|
|
|
printf("samples: 1 << %2d, buckets: 1 << %2d, var_coeff: %0.3f\n",
|
|
samples_log2, num_buckets_log2, variation_coefficient);
|
|
CHECK_LT(variation_coefficient, max_var);
|
|
}
|
|
uint32_t HalfSipHash(uint32_t key, uint64_t seed) {
|
|
return halfsiphash(key, seed);
|
|
}
|
|
|
|
uint32_t JenkinsHash(uint32_t key, uint64_t seed) {
|
|
return ComputeLongHash(static_cast<uint64_t>(key) ^ seed);
|
|
}
|
|
|
|
uint32_t DefaultHash(uint32_t key, uint64_t seed) {
|
|
return ComputeSeededHash(key, seed);
|
|
}
|
|
} // anonymous namespace
|
|
|
|
void TestIntegerHashQuality(HashFunction hash_function) {
|
|
TestIntegerHashQuality(17, 13, 0x123456789ABCDEFU, 0.4, hash_function);
|
|
TestIntegerHashQuality(16, 12, 0x123456789ABCDEFU, 0.4, hash_function);
|
|
TestIntegerHashQuality(15, 11, 0xFEDCBA987654321U, 0.4, hash_function);
|
|
TestIntegerHashQuality(14, 10, 0xFEDCBA987654321U, 0.4, hash_function);
|
|
TestIntegerHashQuality(13, 9, 1, 0.4, hash_function);
|
|
TestIntegerHashQuality(12, 8, 1, 0.4, hash_function);
|
|
|
|
TestIntegerHashQuality(17, 10, 0x123456789ABCDEFU, 0.2, hash_function);
|
|
TestIntegerHashQuality(16, 9, 0x123456789ABCDEFU, 0.2, hash_function);
|
|
TestIntegerHashQuality(15, 8, 0xFEDCBA987654321U, 0.2, hash_function);
|
|
TestIntegerHashQuality(14, 7, 0xFEDCBA987654321U, 0.2, hash_function);
|
|
TestIntegerHashQuality(13, 6, 1, 0.2, hash_function);
|
|
TestIntegerHashQuality(12, 5, 1, 0.2, hash_function);
|
|
}
|
|
|
|
TEST(HalfSipHashQuality) { TestIntegerHashQuality(HalfSipHash); }
|
|
|
|
TEST(JenkinsHashQuality) { TestIntegerHashQuality(JenkinsHash); }
|
|
|
|
TEST(DefaultHashQuality) { TestIntegerHashQuality(DefaultHash); }
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|