Index: src/heap/identity-map.cc |
diff --git a/src/heap/identity-map.cc b/src/heap/identity-map.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6bed98dfaac6ba2f67fcddbb14c24ef95b009496 |
--- /dev/null |
+++ b/src/heap/identity-map.cc |
@@ -0,0 +1,190 @@ |
+// Copyright 2015 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 "src/v8.h" |
+ |
+#include "src/heap/heap.h" |
+#include "src/heap/identity-map.h" |
+ |
+namespace v8 { |
+namespace internal { |
+ |
+static const int kInitialIdentityMapSize = 32; |
+ |
+IdentityMapBase::~IdentityMapBase() { |
+ if (keys_) { |
+ Heap::OptionalRelocationLock lock(heap_, concurrent_); |
+ heap_->UnregisterStrongRoots(keys_); |
+ } |
+} |
+ |
+ |
+IdentityMapBase::RawEntry IdentityMapBase::Lookup(Handle<Object> handle) { |
+ AllowHandleDereference for_lookup; |
+ int index = LookupIndex(*handle); |
+ return index >= 0 ? &values_[index] : nullptr; |
+} |
+ |
+ |
+IdentityMapBase::RawEntry IdentityMapBase::Insert(Handle<Object> handle) { |
+ AllowHandleDereference for_lookup; |
+ int index = InsertIndex(*handle); |
+ DCHECK_GE(index, 0); |
+ return &values_[index]; |
+} |
+ |
+ |
+int IdentityMapBase::Hash(Object* address) { |
+ uintptr_t raw_address = reinterpret_cast<uintptr_t>(address); |
+ CHECK_NE(0, raw_address); // cannot store Smi 0 as a key here, sorry. |
Hannes Payer (out of office)
2015/04/27 15:06:32
Cannot
titzer
2015/04/27 16:32:50
Done.
|
+ // xor some of the upper bits, since the lower 2 or 3 are usually aligned. |
Erik Corry
2015/04/27 15:39:13
Xor
Also see comments below with missing caps and
titzer
2015/04/27 16:32:50
Done.
|
+ return static_cast<int>((raw_address >> 11) ^ raw_address); |
+} |
+ |
+ |
+int IdentityMapBase::LookupIndex(Object* address) { |
+ int start = Hash(address) & mask_; |
+ for (int index = start; index < size_; index++) { |
+ if (keys_[index] == address) return index; // found |
+ if (keys_[index] == nullptr) return -1; // not found |
+ } |
+ for (int index = 0; index < start; index++) { |
+ if (keys_[index] == address) return index; // found |
+ if (keys_[index] == nullptr) return -1; // not found |
+ } |
+ return -1; |
+} |
+ |
+ |
+int IdentityMapBase::InsertIndex(Object* address) { |
+ while (true) { |
+ int start = Hash(address) & mask_; |
+ int limit = size_ / 2; |
+ for (int index = start; index < size_; index++) { |
Erik Corry
2015/04/27 15:39:13
For hashes that are near the end of the array we w
titzer
2015/04/27 16:32:51
Good catch. I wrap the search now.
|
+ if (keys_[index] == address) return index; // found |
+ if (keys_[index] == nullptr) { // free entry |
+ keys_[index] = address; |
+ return index; |
+ } |
+ if (--limit == 0) break; // searched too far; resize to insert. |
Hannes Payer (out of office)
2015/04/27 15:06:32
Searched
titzer
2015/04/27 16:32:51
Done.
|
+ } |
+ Resize(); // Should only have to resize once, since we grow 4x. |
Hannes Payer (out of office)
2015/04/27 15:06:32
4 = kResizeFactor
Erik Corry
2015/04/27 15:39:13
That's very aggressive. Was 2x too slow?
Also thi
titzer
2015/04/27 16:32:50
4x guarantees that there are no chains that size_
Erik Corry
2015/04/28 09:32:34
Rather than cuckoo I would go with Robin Hood hash
|
+ } |
+ UNREACHABLE(); |
+ return -1; |
+} |
+ |
+ |
+// Searches this map for the given key using the object's address |
+// as the identity, returning: |
+// found => a pointer to the storage location for the value |
+// not found => a pointer to a new storage location for the value |
+IdentityMapBase::RawEntry IdentityMapBase::GetEntry(Handle<Object> handle) { |
+ Heap::OptionalRelocationLock lock(heap_, concurrent_); |
+ RawEntry result; |
+ if (size_ == 0) { |
+ // Allocate the initial storage for keys and values. |
+ size_ = kInitialIdentityMapSize; |
+ mask_ = kInitialIdentityMapSize - 1; |
+ gc_counter_ = heap_->gc_count(); |
+ |
+ keys_ = zone_->NewArray<Object*>(size_); |
+ memset(keys_, 0, sizeof(Object*) * size_); |
+ values_ = zone_->NewArray<void*>(size_); |
+ memset(values_, 0, sizeof(void*) * size_); |
+ |
+ heap_->RegisterStrongRoots(keys_, keys_ + size_); |
+ result = Insert(handle); |
+ } else { |
+ // Perform an optimistic lookup. |
+ result = Lookup(handle); |
+ if (result == nullptr) { |
+ // Miss; rehash if there was a GC, then insert. |
+ if (gc_counter_ != heap_->gc_count()) Rehash(); |
+ result = Insert(handle); |
+ } |
+ } |
+ return result; |
+} |
+ |
+ |
+// Searches this map for the given key using the object's address |
+// as the identity, returning: |
+// found => a pointer to the storage location for the value |
+// not found => {nullptr} |
+IdentityMapBase::RawEntry IdentityMapBase::FindEntry(Handle<Object> handle) { |
+ if (size_ == 0) return nullptr; |
+ |
+ Heap::OptionalRelocationLock lock(heap_, concurrent_); |
+ RawEntry result = Lookup(handle); |
+ if (result == nullptr && gc_counter_ != heap_->gc_count()) { |
+ Rehash(); // Rehash is expensive, so only do it in case of a miss. |
+ result = Lookup(handle); |
+ } |
+ return result; |
+} |
+ |
+ |
+void IdentityMapBase::Rehash() { |
Hannes Payer (out of office)
2015/04/27 15:06:32
Can we assert here that we have the relocation loc
titzer
2015/04/27 16:32:50
Unfortunately that API appears to have been refact
|
+ // Record the current GC counter. |
+ gc_counter_ = heap_->gc_count(); |
+ // Assume that most objects won't be moved. |
+ ZoneVector<std::pair<Object*, void*>> reinsert(zone_); |
+ // Search the table looking for keys that wouldn't be found with their |
+ // current hashcode and evacuate them. |
+ int last_empty = -1; |
+ for (int i = 0; i < size_; i++) { |
+ if (keys_[i] == nullptr) { |
+ last_empty = i; |
+ } else { |
+ int pos = Hash(keys_[i]) & mask_; |
+ if (pos <= last_empty || pos > i) { |
+ // Evacuate an entry that is in the wrong place. |
+ reinsert.push_back(std::pair<Object*, void*>(keys_[i], values_[i])); |
+ keys_[i] = nullptr; |
+ values_[i] = nullptr; |
+ last_empty = i; |
+ } |
+ } |
+ } |
+ // Reinsert all the key/value pairs that were in the wrong place. |
+ for (auto pair : reinsert) { |
+ int index = InsertIndex(pair.first); |
+ DCHECK_GE(index, 0); |
+ DCHECK_NULL(values_[index]); |
+ values_[index] = pair.second; |
+ } |
+} |
+ |
+ |
+void IdentityMapBase::Resize() { |
+ // Grow the internal storage and reinsert all the key/value pairs. |
+ int old_size = size_; |
+ Object** old_keys = keys_; |
+ void** old_values = values_; |
+ |
+ size_ = size_ * 4; |
Hannes Payer (out of office)
2015/04/27 15:06:32
4 = kResizeFactor; make this a constant
titzer
2015/04/27 16:32:50
Done.
|
+ mask_ = size_ - 1; |
+ gc_counter_ = heap_->gc_count(); |
+ |
+ CHECK_LE(size_, (1024 * 1024 * 16)); // that would be extreme... |
+ |
+ keys_ = zone_->NewArray<Object*>(size_); |
+ memset(keys_, 0, sizeof(Object*) * size_); |
+ values_ = zone_->NewArray<void*>(size_); |
+ memset(values_, 0, sizeof(void*) * size_); |
+ |
+ for (int i = 0; i < old_size; i++) { |
+ if (old_keys[i] == nullptr) continue; |
+ int index = InsertIndex(old_keys[i]); |
+ DCHECK_GE(index, 0); |
+ values_[index] = old_values[i]; |
+ } |
+ |
+ // Unregister old keys and register new keys. |
+ heap_->UnregisterStrongRoots(old_keys); |
+ heap_->RegisterStrongRoots(keys_, keys_ + size_); |
+} |
+} |
+} // namespace v8::internal |