mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:59:47 +00:00
[vm/aot] Improve AOT compilation speed by using better hash codes
This change improves hash code implementations in multiple places in the compiler. That reduces number of probes during lookups in hash maps and improves AOT compilation time of large applications. On a large Flutter app, compiled in release mode for arm64: Total gen_snapshot time 89.184s -> 60.736s (-31.9%) Also, this change adds --hash_map_probes_limit=N option which sets a hard limit for the number of probes in hash maps. This option makes it easy to find hash maps where there are many collisions due to poor hash code implementation. TEST=ci Issue: https://github.com/dart-lang/sdk/issues/43299 Bug: b/154155290 Change-Id: Ibf6f37d4b9f3bf42dd6731bfb4095a7305b98b2d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/229240 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
d17205184a
commit
2a34453dd1
|
@ -219,7 +219,7 @@ class InstanceKeyValueTrait {
|
|||
|
||||
static Value ValueOf(Pair kv) { return kv; }
|
||||
|
||||
static inline uword Hash(Key key) { return key->GetClassId(); }
|
||||
static inline uword Hash(Key key) { return key->CanonicalizeHash(); }
|
||||
|
||||
static inline bool IsKeyEqual(Pair pair, Key key) {
|
||||
return pair->ptr() == key->ptr();
|
||||
|
|
|
@ -101,6 +101,11 @@ intptr_t ObjectHash(const Object& obj) {
|
|||
if (obj.IsNull()) {
|
||||
return kNullIdentityHash;
|
||||
}
|
||||
// TypeArguments should be handled before Instance as TypeArguments extends
|
||||
// Instance and TypeArguments::CanonicalizeHash just returns 0.
|
||||
if (obj.IsTypeArguments()) {
|
||||
return TypeArguments::Cast(obj).Hash();
|
||||
}
|
||||
if (obj.IsInstance()) {
|
||||
return Instance::Cast(obj).CanonicalizeHash();
|
||||
}
|
||||
|
@ -121,6 +126,10 @@ intptr_t ObjectHash(const Object& obj) {
|
|||
return obj.GetClassId();
|
||||
}
|
||||
|
||||
const char* ObjectToCString(const Object& obj) {
|
||||
return obj.ToCString();
|
||||
}
|
||||
|
||||
void SetToNull(Object* obj) {
|
||||
*obj = Object::null();
|
||||
}
|
||||
|
|
|
@ -181,6 +181,9 @@ inline const Object& ToObject(const Function& handle) {
|
|||
// or canonical hash.
|
||||
intptr_t ObjectHash(const Object& obj);
|
||||
|
||||
// Prints the given object into a C string.
|
||||
const char* ObjectToCString(const Object& obj);
|
||||
|
||||
// If the given object represents a Dart integer returns true and sets [value]
|
||||
// to the value of the integer.
|
||||
bool HasIntegerValue(const dart::Object& obj, int64_t* value);
|
||||
|
|
|
@ -143,6 +143,8 @@ constexpr bool FLAG_support_il_printer = false;
|
|||
P(marker_tasks, int, 2, \
|
||||
"The number of tasks to spawn during old gen GC marking (0 means " \
|
||||
"perform all marking on main thread).") \
|
||||
P(hash_map_probes_limit, int, kMaxInt32, \
|
||||
"Limit number of probes while doing lookups in hash maps.") \
|
||||
P(max_polymorphic_checks, int, 4, \
|
||||
"Maximum number of polymorphic check, otherwise it is megamorphic.") \
|
||||
P(max_equality_polymorphic_checks, int, 32, \
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define RUNTIME_VM_HASH_MAP_H_
|
||||
|
||||
#include "platform/utils.h"
|
||||
#include "vm/flags.h"
|
||||
#include "vm/growable_array.h" // For Malloc, EmptyBase
|
||||
#include "vm/hash.h"
|
||||
#include "vm/zone.h"
|
||||
|
@ -132,6 +133,7 @@ BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>::Lookup(
|
|||
uint32_t mask = hash_table_size_ - 1;
|
||||
uint32_t hash_index = hash & mask;
|
||||
uint32_t start = hash_index;
|
||||
intptr_t probes = 0;
|
||||
for (;;) {
|
||||
uint32_t pair_index = hash_table_[hash_index];
|
||||
if (pair_index == kEmpty) {
|
||||
|
@ -139,6 +141,7 @@ BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>::Lookup(
|
|||
}
|
||||
if (pair_index != kDeleted) {
|
||||
ASSERT(pair_index < pairs_size_);
|
||||
RELEASE_ASSERT(++probes < FLAG_hash_map_probes_limit);
|
||||
if (KeyValueTrait::IsKeyEqual(pairs_[pair_index], key)) {
|
||||
return &pairs_[pair_index];
|
||||
}
|
||||
|
@ -233,6 +236,7 @@ void BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>::Insert(
|
|||
uint32_t mask = hash_table_size_ - 1;
|
||||
uint32_t hash_index = hash & mask;
|
||||
uint32_t start = hash_index;
|
||||
intptr_t probes = 0;
|
||||
for (;;) {
|
||||
uint32_t pair_index = hash_table_[hash_index];
|
||||
if ((pair_index == kEmpty) || (pair_index == kDeleted)) {
|
||||
|
@ -241,6 +245,7 @@ void BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>::Insert(
|
|||
next_pair_index_++;
|
||||
break;
|
||||
}
|
||||
RELEASE_ASSERT(++probes < FLAG_hash_map_probes_limit);
|
||||
ASSERT(pair_index < pairs_size_);
|
||||
hash_index = (hash_index + 1) & mask;
|
||||
// Hashtable must contain at least one empty marker.
|
||||
|
@ -273,6 +278,7 @@ bool BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>::Remove(
|
|||
uint32_t mask = hash_table_size_ - 1;
|
||||
uint32_t hash_index = hash & mask;
|
||||
uint32_t start = hash_index;
|
||||
intptr_t probes = 0;
|
||||
for (;;) {
|
||||
uint32_t pair_index = hash_table_[hash_index];
|
||||
if (pair_index == kEmpty) {
|
||||
|
@ -280,6 +286,7 @@ bool BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>::Remove(
|
|||
}
|
||||
if (pair_index != kDeleted) {
|
||||
ASSERT(pair_index < pairs_size_);
|
||||
RELEASE_ASSERT(++probes < FLAG_hash_map_probes_limit);
|
||||
if (KeyValueTrait::IsKeyEqual(pairs_[pair_index], key)) {
|
||||
hash_table_[hash_index] = kDeleted;
|
||||
pairs_[pair_index] = typename KeyValueTrait::Pair();
|
||||
|
|
|
@ -5887,6 +5887,11 @@ class CodeSourceMap : public Object {
|
|||
return memcmp(untag(), other.untag(), InstanceSize(Length())) == 0;
|
||||
}
|
||||
|
||||
uint32_t Hash() const {
|
||||
NoSafepointScope no_safepoint;
|
||||
return HashBytes(Data(), Length());
|
||||
}
|
||||
|
||||
void PrintToJSONObject(JSONObject* jsobj, bool ref) const;
|
||||
|
||||
private:
|
||||
|
|
|
@ -1020,7 +1020,7 @@ class CodeSourceMapKeyValueTrait {
|
|||
|
||||
static inline uword Hash(Key key) {
|
||||
ASSERT(!key->IsNull());
|
||||
return Utils::WordHash(key->Length());
|
||||
return Utils::WordHash(key->Hash());
|
||||
}
|
||||
|
||||
static inline bool IsKeyEqual(Pair pair, Key key) {
|
||||
|
@ -1070,7 +1070,14 @@ class ArrayKeyValueTrait {
|
|||
|
||||
static inline uword Hash(Key key) {
|
||||
ASSERT(!key->IsNull());
|
||||
return Utils::WordHash(key->Length());
|
||||
ASSERT(Thread::Current()->no_safepoint_scope_depth() > 0);
|
||||
const intptr_t len = key->Length();
|
||||
uint32_t hash = Utils::WordHash(len);
|
||||
for (intptr_t i = 0; i < len; ++i) {
|
||||
hash =
|
||||
CombineHashes(hash, Utils::WordHash(static_cast<uword>(key->At(i))));
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static inline bool IsKeyEqual(Pair pair, Key key) {
|
||||
|
@ -1128,6 +1135,9 @@ void ProgramVisitor::DedupLists(Thread* thread) {
|
|||
};
|
||||
|
||||
StackZone stack_zone(thread);
|
||||
// ArrayKeyValueTrait::Hash is based on object addresses, so make
|
||||
// sure GC doesn't happen and doesn't move objects.
|
||||
NoSafepointScope no_safepoint;
|
||||
DedupListsVisitor visitor(thread->zone());
|
||||
WalkProgram(thread->zone(), thread->isolate_group(), &visitor);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue