Changed hash table to use more of the hash value when probing.
Review URL: http://codereview.chromium.org/155350 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2486 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
263acb4d02
commit
e0047e4331
@ -103,16 +103,20 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
|
|||||||
// Generate an unrolled loop that performs a few probes before
|
// Generate an unrolled loop that performs a few probes before
|
||||||
// giving up. Measurements done on Gmail indicate that 2 probes
|
// giving up. Measurements done on Gmail indicate that 2 probes
|
||||||
// cover ~93% of loads from dictionaries.
|
// cover ~93% of loads from dictionaries.
|
||||||
static const int kProbes = 4;
|
static const uint32_t kProbes =
|
||||||
for (int i = 0; i < kProbes; i++) {
|
HashTable<StringDictionaryShape, String*>::kNofFastProbes;
|
||||||
// Compute the masked index: (hash + i + i * i) & mask.
|
static const uint32_t kShift =
|
||||||
|
HashTable<StringDictionaryShape, String*>::kHashRotateShift;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < kProbes; i++) {
|
||||||
|
// Compute the masked index.
|
||||||
__ ldr(t1, FieldMemOperand(r2, String::kLengthOffset));
|
__ ldr(t1, FieldMemOperand(r2, String::kLengthOffset));
|
||||||
__ mov(t1, Operand(t1, LSR, String::kHashShift));
|
__ mov(t1, Operand(t1, LSR, String::kHashShift));
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
__ add(t1, t1, Operand(StringDictionary::GetProbeOffset(i)));
|
__ and_(t1, r3, Operand(t1, ROR, (kShift * i) % kBitsPerInt));
|
||||||
}
|
} else {
|
||||||
__ and_(t1, t1, Operand(r3));
|
__ and_(t1, t1, Operand(r3));
|
||||||
|
}
|
||||||
// Scale the index by multiplying by the element size.
|
// Scale the index by multiplying by the element size.
|
||||||
ASSERT(StringDictionary::kEntrySize == 3);
|
ASSERT(StringDictionary::kEntrySize == 3);
|
||||||
__ add(t1, t1, Operand(t1, LSL, 1)); // t1 = t1 * 3
|
__ add(t1, t1, Operand(t1, LSL, 1)); // t1 = t1 * 3
|
||||||
|
@ -45,6 +45,7 @@ using ::v8::internal::PrintF;
|
|||||||
using ::v8::internal::OS;
|
using ::v8::internal::OS;
|
||||||
using ::v8::internal::ReadLine;
|
using ::v8::internal::ReadLine;
|
||||||
using ::v8::internal::DeleteArray;
|
using ::v8::internal::DeleteArray;
|
||||||
|
using ::v8::internal::RotateRight;
|
||||||
|
|
||||||
// This macro provides a platform independent use of sscanf. The reason for
|
// This macro provides a platform independent use of sscanf. The reason for
|
||||||
// SScanF not being implemented in a platform independent was through
|
// SScanF not being implemented in a platform independent was through
|
||||||
@ -817,7 +818,12 @@ int32_t Simulator::GetShiftRm(Instr* instr, bool* carry_out) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case ROR: {
|
case ROR: {
|
||||||
UNIMPLEMENTED();
|
ASSERT(shift_amount > 0);
|
||||||
|
uint32_t uresult = static_cast<uint32_t>(result);
|
||||||
|
uresult = RotateRight(uresult, shift_amount - 1);
|
||||||
|
*carry_out = (uresult & 1) == 1;
|
||||||
|
uresult = RotateRight(uresult, 1);
|
||||||
|
result = static_cast<int32_t>(uresult);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1240,6 +1240,16 @@ void Assembler::xor_(const Operand& dst, const Immediate& x) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Assembler::ror(Register dst, uint32_t count) {
|
||||||
|
EnsureSpace ensure_space(this);
|
||||||
|
last_pc_ = pc_;
|
||||||
|
EMIT(0xC1);
|
||||||
|
emit_operand(ecx, Operand(dst));
|
||||||
|
ASSERT(count < 32);
|
||||||
|
EMIT(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Assembler::bt(const Operand& dst, Register src) {
|
void Assembler::bt(const Operand& dst, Register src) {
|
||||||
EnsureSpace ensure_space(this);
|
EnsureSpace ensure_space(this);
|
||||||
last_pc_ = pc_;
|
last_pc_ = pc_;
|
||||||
|
@ -597,6 +597,9 @@ class Assembler : public Malloced {
|
|||||||
void xor_(const Operand& src, Register dst);
|
void xor_(const Operand& src, Register dst);
|
||||||
void xor_(const Operand& dst, const Immediate& x);
|
void xor_(const Operand& dst, const Immediate& x);
|
||||||
|
|
||||||
|
// Rotate dist right count times, asserts count < 32.
|
||||||
|
void ror(Register dst, uint32_t count);
|
||||||
|
|
||||||
// Bit operations.
|
// Bit operations.
|
||||||
void bt(const Operand& dst, Register src);
|
void bt(const Operand& dst, Register src);
|
||||||
void bts(const Operand& dst, Register src);
|
void bts(const Operand& dst, Register src);
|
||||||
|
@ -97,15 +97,20 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label,
|
|||||||
// Generate an unrolled loop that performs a few probes before
|
// Generate an unrolled loop that performs a few probes before
|
||||||
// giving up. Measurements done on Gmail indicate that 2 probes
|
// giving up. Measurements done on Gmail indicate that 2 probes
|
||||||
// cover ~93% of loads from dictionaries.
|
// cover ~93% of loads from dictionaries.
|
||||||
static const int kProbes = 4;
|
|
||||||
const int kElementsStartOffset =
|
const int kElementsStartOffset =
|
||||||
Array::kHeaderSize + StringDictionary::kElementsStartIndex * kPointerSize;
|
Array::kHeaderSize + StringDictionary::kElementsStartIndex * kPointerSize;
|
||||||
for (int i = 0; i < kProbes; i++) {
|
|
||||||
// Compute the masked index: (hash + i + i * i) & mask.
|
static const uint32_t kProbes =
|
||||||
|
HashTable<StringDictionaryShape, String*>::kNofFastProbes;
|
||||||
|
static const uint32_t kShift =
|
||||||
|
HashTable<StringDictionaryShape, String*>::kHashRotateShift;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < kProbes; i++) {
|
||||||
|
// Compute the masked index.
|
||||||
__ mov(r1, FieldOperand(name, String::kLengthOffset));
|
__ mov(r1, FieldOperand(name, String::kLengthOffset));
|
||||||
__ shr(r1, String::kHashShift);
|
__ shr(r1, String::kHashShift);
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
__ add(Operand(r1), Immediate(StringDictionary::GetProbeOffset(i)));
|
__ ror(r1, (kShift * i) % kBitsPerInt);
|
||||||
}
|
}
|
||||||
__ and_(r1, Operand(r2));
|
__ and_(r1, Operand(r2));
|
||||||
|
|
||||||
|
@ -6502,7 +6502,8 @@ template<typename Shape, typename Key>
|
|||||||
Object* HashTable<Shape, Key>::Allocate(
|
Object* HashTable<Shape, Key>::Allocate(
|
||||||
int at_least_space_for) {
|
int at_least_space_for) {
|
||||||
int capacity = RoundUpToPowerOf2(at_least_space_for);
|
int capacity = RoundUpToPowerOf2(at_least_space_for);
|
||||||
if (capacity < 4) capacity = 4; // Guarantee min capacity.
|
static const int kMinCapacity = 16;
|
||||||
|
if (capacity < kMinCapacity) capacity = kMinCapacity;
|
||||||
Object* obj = Heap::AllocateHashTable(EntryToIndex(capacity));
|
Object* obj = Heap::AllocateHashTable(EntryToIndex(capacity));
|
||||||
if (!obj->IsFailure()) {
|
if (!obj->IsFailure()) {
|
||||||
HashTable::cast(obj)->SetNumberOfElements(0);
|
HashTable::cast(obj)->SetNumberOfElements(0);
|
||||||
@ -6516,26 +6517,25 @@ Object* HashTable<Shape, Key>::Allocate(
|
|||||||
// Find entry for key otherwise return -1.
|
// Find entry for key otherwise return -1.
|
||||||
template<typename Shape, typename Key>
|
template<typename Shape, typename Key>
|
||||||
int HashTable<Shape, Key>::FindEntry(Key key) {
|
int HashTable<Shape, Key>::FindEntry(Key key) {
|
||||||
uint32_t nof = NumberOfElements();
|
uint32_t mask = Capacity() - 1;
|
||||||
if (nof == 0) return kNotFound; // Bail out if empty.
|
|
||||||
|
|
||||||
uint32_t capacity = Capacity();
|
|
||||||
uint32_t hash = Shape::Hash(key);
|
uint32_t hash = Shape::Hash(key);
|
||||||
uint32_t entry = GetProbe(hash, 0, capacity);
|
|
||||||
|
|
||||||
|
// For the first probes rotate the hash to ensure a proper spread.
|
||||||
|
for (uint32_t i = 0; i < kNofFastProbes; i++) {
|
||||||
|
int entry = hash & mask;
|
||||||
Object* element = KeyAt(entry);
|
Object* element = KeyAt(entry);
|
||||||
uint32_t passed_elements = 0;
|
if (element->IsUndefined()) return kNotFound;
|
||||||
if (!element->IsNull()) {
|
if (!element->IsNull() && Shape::IsMatch(key, element)) return entry;
|
||||||
if (!element->IsUndefined() && Shape::IsMatch(key, element)) return entry;
|
hash = RotateRight(hash, kHashRotateShift);
|
||||||
if (++passed_elements == nof) return kNotFound;
|
|
||||||
}
|
|
||||||
for (uint32_t i = 1; !element->IsUndefined(); i++) {
|
|
||||||
entry = GetProbe(hash, i, capacity);
|
|
||||||
element = KeyAt(entry);
|
|
||||||
if (!element->IsNull()) {
|
|
||||||
if (!element->IsUndefined() && Shape::IsMatch(key, element)) return entry;
|
|
||||||
if (++passed_elements == nof) return kNotFound;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// In this unlikely event, do a linear scan.
|
||||||
|
for (uint32_t i = 1; i <= mask; i++) {
|
||||||
|
int entry = ++hash & mask;
|
||||||
|
Object* element = KeyAt(entry);
|
||||||
|
if (element->IsUndefined()) return kNotFound;
|
||||||
|
if (!element->IsNull() && Shape::IsMatch(key, element)) return entry;
|
||||||
}
|
}
|
||||||
return kNotFound;
|
return kNotFound;
|
||||||
}
|
}
|
||||||
@ -6579,15 +6579,23 @@ Object* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
|
|||||||
|
|
||||||
template<typename Shape, typename Key>
|
template<typename Shape, typename Key>
|
||||||
uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
|
uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
|
||||||
uint32_t capacity = Capacity();
|
uint32_t mask = Capacity() - 1;
|
||||||
uint32_t entry = GetProbe(hash, 0, capacity);
|
int entry;
|
||||||
Object* element = KeyAt(entry);
|
Object* element;
|
||||||
|
|
||||||
for (uint32_t i = 1; !(element->IsUndefined() || element->IsNull()); i++) {
|
// For the first probes rotate the hash to ensure a proper spread.
|
||||||
entry = GetProbe(hash, i, capacity);
|
for (uint32_t i = 0; i < kNofFastProbes; i++) {
|
||||||
|
entry = hash & mask;
|
||||||
element = KeyAt(entry);
|
element = KeyAt(entry);
|
||||||
|
if (element->IsUndefined() || element->IsNull()) return entry;
|
||||||
|
hash = RotateRight(hash, kHashRotateShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
entry = ++hash & mask;
|
||||||
|
element = KeyAt(entry);
|
||||||
|
} while(!(element->IsUndefined() || element->IsNull()));
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6666,6 +6674,10 @@ int Dictionary<NumberDictionaryShape, uint32_t>::NumberOfEnumElements();
|
|||||||
template
|
template
|
||||||
int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
|
int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
|
||||||
|
|
||||||
|
template
|
||||||
|
int HashTable<NumberDictionaryShape, uint32_t>::FindEntry(uint32_t key);
|
||||||
|
|
||||||
|
|
||||||
// Collates undefined and unexisting elements below limit from position
|
// Collates undefined and unexisting elements below limit from position
|
||||||
// zero of the elements. The object stays in Dictionary mode.
|
// zero of the elements. The object stays in Dictionary mode.
|
||||||
Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
|
Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
|
||||||
@ -7021,7 +7033,7 @@ class SymbolsKey : public HashTableKey {
|
|||||||
uint32_t HashForObject(Object* obj) {
|
uint32_t HashForObject(Object* obj) {
|
||||||
FixedArray* symbols = FixedArray::cast(obj);
|
FixedArray* symbols = FixedArray::cast(obj);
|
||||||
int len = symbols->length();
|
int len = symbols->length();
|
||||||
uint32_t hash = 0;
|
uint32_t hash = 40617523; // In case the array is empty.
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
hash ^= String::cast(symbols->get(i))->Hash();
|
hash ^= String::cast(symbols->get(i))->Hash();
|
||||||
}
|
}
|
||||||
|
@ -2051,11 +2051,6 @@ class HashTable: public FixedArray {
|
|||||||
// Casting.
|
// Casting.
|
||||||
static inline HashTable* cast(Object* obj);
|
static inline HashTable* cast(Object* obj);
|
||||||
|
|
||||||
// Compute the probe offset (quadratic probing).
|
|
||||||
INLINE(static uint32_t GetProbeOffset(uint32_t n)) {
|
|
||||||
return (n + n * n) >> 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const int kNumberOfElementsIndex = 0;
|
static const int kNumberOfElementsIndex = 0;
|
||||||
static const int kCapacityIndex = 1;
|
static const int kCapacityIndex = 1;
|
||||||
static const int kPrefixStartIndex = 2;
|
static const int kPrefixStartIndex = 2;
|
||||||
@ -2071,6 +2066,9 @@ class HashTable: public FixedArray {
|
|||||||
// Find entry for key otherwise return -1.
|
// Find entry for key otherwise return -1.
|
||||||
int FindEntry(Key key);
|
int FindEntry(Key key);
|
||||||
|
|
||||||
|
static const uint32_t kNofFastProbes = 4;
|
||||||
|
static const uint32_t kHashRotateShift = 3;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// Find the entry at which to insert element with the given key that
|
// Find the entry at which to insert element with the given key that
|
||||||
@ -2096,13 +2094,6 @@ class HashTable: public FixedArray {
|
|||||||
fast_set(this, kCapacityIndex, Smi::FromInt(capacity));
|
fast_set(this, kCapacityIndex, Smi::FromInt(capacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Returns probe entry.
|
|
||||||
static uint32_t GetProbe(uint32_t hash, uint32_t number, uint32_t size) {
|
|
||||||
ASSERT(IsPowerOf2(size));
|
|
||||||
return (hash + GetProbeOffset(number)) & (size - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure enough space for n additional elements.
|
// Ensure enough space for n additional elements.
|
||||||
Object* EnsureCapacity(int n, Key key);
|
Object* EnsureCapacity(int n, Key key);
|
||||||
};
|
};
|
||||||
|
15
src/utils.h
15
src/utils.h
@ -211,6 +211,21 @@ inline byte* DecodeUnsignedIntBackward(byte* p, unsigned int* x) {
|
|||||||
uint32_t ComputeIntegerHash(uint32_t key);
|
uint32_t ComputeIntegerHash(uint32_t key);
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Bitwise rotate word
|
||||||
|
|
||||||
|
inline uint32_t RotateRight(uint32_t value, uint32_t n) {
|
||||||
|
ASSERT(n < 31);
|
||||||
|
return (value >> n) | (value << (32-n));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline uint32_t RotateLeft(uint32_t value, uint32_t n) {
|
||||||
|
ASSERT(n < 31);
|
||||||
|
return (value << n) | (value >> (32-n));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// I/O support.
|
// I/O support.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user