Commit d4f3da01 authored by Ulan Degenbaev's avatar Ulan Degenbaev Committed by Commit Bot

[heap] Optimize handling of data only objects in Scavenger

This patch reduces the cost of the predicate that computes whether an
object contains only data or may contain pointers.

This also guards pushing to the copy_list_ with the predicate.

Bug: chromium:852420
Change-Id: I55c4e15eb8341708a21f484fb95b2c2cc2b25143
Reviewed-on: https://chromium-review.googlesource.com/c/1430068
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59074}
parent 460bf85c
...@@ -5515,6 +5515,11 @@ void VerifyPointersVisitor::VerifyPointersImpl(TSlot start, TSlot end) { ...@@ -5515,6 +5515,11 @@ void VerifyPointersVisitor::VerifyPointersImpl(TSlot start, TSlot end) {
void VerifyPointersVisitor::VerifyPointers(HeapObject host, void VerifyPointersVisitor::VerifyPointers(HeapObject host,
MaybeObjectSlot start, MaybeObjectSlot start,
MaybeObjectSlot end) { MaybeObjectSlot end) {
// If this DCHECK fires then you probably added a pointer field
// to one of objects in DATA_ONLY_VISITOR_ID_LIST. You can fix
// this by moving that object to POINTER_VISITOR_ID_LIST.
DCHECK_EQ(ObjectFields::kMaybePointers,
Map::ObjectFieldsFrom(host->map()->visitor_id()));
VerifyPointersImpl(start, end); VerifyPointersImpl(start, end);
} }
......
...@@ -59,6 +59,7 @@ ResultType HeapVisitor<ResultType, ConcreteVisitor>::Visit(Map map, ...@@ -59,6 +59,7 @@ ResultType HeapVisitor<ResultType, ConcreteVisitor>::Visit(Map map,
return visitor->VisitFreeSpace(map, FreeSpace::cast(object)); return visitor->VisitFreeSpace(map, FreeSpace::cast(object));
case kVisitWeakArray: case kVisitWeakArray:
return visitor->VisitWeakArray(map, object); return visitor->VisitWeakArray(map, object);
case kDataOnlyVisitorIdCount:
case kVisitorIdCount: case kVisitorIdCount:
UNREACHABLE(); UNREACHABLE();
} }
......
...@@ -91,25 +91,6 @@ bool Scavenger::PromotionList::ShouldEagerlyProcessPromotionList(int task_id) { ...@@ -91,25 +91,6 @@ bool Scavenger::PromotionList::ShouldEagerlyProcessPromotionList(int task_id) {
return LocalPushSegmentSize(task_id) < kProcessPromotionListThreshold; return LocalPushSegmentSize(task_id) < kProcessPromotionListThreshold;
} }
// White list for objects that for sure only contain data.
bool Scavenger::ContainsOnlyData(VisitorId visitor_id) {
switch (visitor_id) {
case kVisitSeqOneByteString:
return true;
case kVisitSeqTwoByteString:
return true;
case kVisitByteArray:
return true;
case kVisitFixedDoubleArray:
return true;
case kVisitDataObject:
return true;
default:
break;
}
return false;
}
void Scavenger::PageMemoryFence(MaybeObject object) { void Scavenger::PageMemoryFence(MaybeObject object) {
#ifdef THREAD_SANITIZER #ifdef THREAD_SANITIZER
// Perform a dummy acquire load to tell TSAN that there is no data race // Perform a dummy acquire load to tell TSAN that there is no data race
...@@ -148,10 +129,9 @@ bool Scavenger::MigrateObject(Map map, HeapObject source, HeapObject target, ...@@ -148,10 +129,9 @@ bool Scavenger::MigrateObject(Map map, HeapObject source, HeapObject target,
} }
template <typename THeapObjectSlot> template <typename THeapObjectSlot>
CopyAndForwardResult Scavenger::SemiSpaceCopyObject(Map map, CopyAndForwardResult Scavenger::SemiSpaceCopyObject(
THeapObjectSlot slot, Map map, THeapObjectSlot slot, HeapObject object, int object_size,
HeapObject object, ObjectFields object_fields) {
int object_size) {
static_assert(std::is_same<THeapObjectSlot, FullHeapObjectSlot>::value || static_assert(std::is_same<THeapObjectSlot, FullHeapObjectSlot>::value ||
std::is_same<THeapObjectSlot, HeapObjectSlot>::value, std::is_same<THeapObjectSlot, HeapObjectSlot>::value,
"Only FullHeapObjectSlot and HeapObjectSlot are expected here"); "Only FullHeapObjectSlot and HeapObjectSlot are expected here");
...@@ -175,8 +155,9 @@ CopyAndForwardResult Scavenger::SemiSpaceCopyObject(Map map, ...@@ -175,8 +155,9 @@ CopyAndForwardResult Scavenger::SemiSpaceCopyObject(Map map,
: CopyAndForwardResult::SUCCESS_OLD_GENERATION; : CopyAndForwardResult::SUCCESS_OLD_GENERATION;
} }
HeapObjectReference::Update(slot, target); HeapObjectReference::Update(slot, target);
if (object_fields == ObjectFields::kMaybePointers) {
copied_list_.Push(ObjectAndSize(target, object_size)); copied_list_.Push(ObjectAndSize(target, object_size));
}
copied_size_ += object_size; copied_size_ += object_size;
return CopyAndForwardResult::SUCCESS_YOUNG_GENERATION; return CopyAndForwardResult::SUCCESS_YOUNG_GENERATION;
} }
...@@ -186,7 +167,8 @@ CopyAndForwardResult Scavenger::SemiSpaceCopyObject(Map map, ...@@ -186,7 +167,8 @@ CopyAndForwardResult Scavenger::SemiSpaceCopyObject(Map map,
template <typename THeapObjectSlot> template <typename THeapObjectSlot>
CopyAndForwardResult Scavenger::PromoteObject(Map map, THeapObjectSlot slot, CopyAndForwardResult Scavenger::PromoteObject(Map map, THeapObjectSlot slot,
HeapObject object, HeapObject object,
int object_size) { int object_size,
ObjectFields object_fields) {
static_assert(std::is_same<THeapObjectSlot, FullHeapObjectSlot>::value || static_assert(std::is_same<THeapObjectSlot, FullHeapObjectSlot>::value ||
std::is_same<THeapObjectSlot, HeapObjectSlot>::value, std::is_same<THeapObjectSlot, HeapObjectSlot>::value,
"Only FullHeapObjectSlot and HeapObjectSlot are expected here"); "Only FullHeapObjectSlot and HeapObjectSlot are expected here");
...@@ -209,7 +191,7 @@ CopyAndForwardResult Scavenger::PromoteObject(Map map, THeapObjectSlot slot, ...@@ -209,7 +191,7 @@ CopyAndForwardResult Scavenger::PromoteObject(Map map, THeapObjectSlot slot,
: CopyAndForwardResult::SUCCESS_OLD_GENERATION; : CopyAndForwardResult::SUCCESS_OLD_GENERATION;
} }
HeapObjectReference::Update(slot, target); HeapObjectReference::Update(slot, target);
if (!ContainsOnlyData(map->visitor_id())) { if (object_fields == ObjectFields::kMaybePointers) {
promotion_list_.PushRegularObject(target, object_size); promotion_list_.PushRegularObject(target, object_size);
} }
promoted_size_ += object_size; promoted_size_ += object_size;
...@@ -225,7 +207,8 @@ SlotCallbackResult Scavenger::RememberedSetEntryNeeded( ...@@ -225,7 +207,8 @@ SlotCallbackResult Scavenger::RememberedSetEntryNeeded(
: REMOVE_SLOT; : REMOVE_SLOT;
} }
bool Scavenger::HandleLargeObject(Map map, HeapObject object, int object_size) { bool Scavenger::HandleLargeObject(Map map, HeapObject object, int object_size,
ObjectFields object_fields) {
// TODO(hpayer): Make this check size based, i.e. // TODO(hpayer): Make this check size based, i.e.
// object_size > kMaxRegularHeapObjectSize // object_size > kMaxRegularHeapObjectSize
if (V8_UNLIKELY( if (V8_UNLIKELY(
...@@ -237,7 +220,7 @@ bool Scavenger::HandleLargeObject(Map map, HeapObject object, int object_size) { ...@@ -237,7 +220,7 @@ bool Scavenger::HandleLargeObject(Map map, HeapObject object, int object_size) {
map, MapWord::FromForwardingAddress(object).ToMap()) == map) { map, MapWord::FromForwardingAddress(object).ToMap()) == map) {
surviving_new_large_objects_.insert({object, map}); surviving_new_large_objects_.insert({object, map});
if (!ContainsOnlyData(map->visitor_id())) { if (object_fields == ObjectFields::kMaybePointers) {
promotion_list_.PushLargeObject(object, map, object_size); promotion_list_.PushLargeObject(object, map, object_size);
} }
} }
...@@ -247,17 +230,16 @@ bool Scavenger::HandleLargeObject(Map map, HeapObject object, int object_size) { ...@@ -247,17 +230,16 @@ bool Scavenger::HandleLargeObject(Map map, HeapObject object, int object_size) {
} }
template <typename THeapObjectSlot> template <typename THeapObjectSlot>
SlotCallbackResult Scavenger::EvacuateObjectDefault(Map map, SlotCallbackResult Scavenger::EvacuateObjectDefault(
THeapObjectSlot slot, Map map, THeapObjectSlot slot, HeapObject object, int object_size,
HeapObject object, ObjectFields object_fields) {
int object_size) {
static_assert(std::is_same<THeapObjectSlot, FullHeapObjectSlot>::value || static_assert(std::is_same<THeapObjectSlot, FullHeapObjectSlot>::value ||
std::is_same<THeapObjectSlot, HeapObjectSlot>::value, std::is_same<THeapObjectSlot, HeapObjectSlot>::value,
"Only FullHeapObjectSlot and HeapObjectSlot are expected here"); "Only FullHeapObjectSlot and HeapObjectSlot are expected here");
SLOW_DCHECK(object->SizeFromMap(map) == object_size); SLOW_DCHECK(object->SizeFromMap(map) == object_size);
CopyAndForwardResult result; CopyAndForwardResult result;
if (HandleLargeObject(map, object, object_size)) { if (HandleLargeObject(map, object, object_size, object_fields)) {
return REMOVE_SLOT; return REMOVE_SLOT;
} }
...@@ -267,7 +249,7 @@ SlotCallbackResult Scavenger::EvacuateObjectDefault(Map map, ...@@ -267,7 +249,7 @@ SlotCallbackResult Scavenger::EvacuateObjectDefault(Map map,
if (!heap()->ShouldBePromoted(object->address())) { if (!heap()->ShouldBePromoted(object->address())) {
// A semi-space copy may fail due to fragmentation. In that case, we // A semi-space copy may fail due to fragmentation. In that case, we
// try to promote the object. // try to promote the object.
result = SemiSpaceCopyObject(map, slot, object, object_size); result = SemiSpaceCopyObject(map, slot, object, object_size, object_fields);
if (result != CopyAndForwardResult::FAILURE) { if (result != CopyAndForwardResult::FAILURE) {
return RememberedSetEntryNeeded(result); return RememberedSetEntryNeeded(result);
} }
...@@ -276,13 +258,13 @@ SlotCallbackResult Scavenger::EvacuateObjectDefault(Map map, ...@@ -276,13 +258,13 @@ SlotCallbackResult Scavenger::EvacuateObjectDefault(Map map,
// We may want to promote this object if the object was already semi-space // We may want to promote this object if the object was already semi-space
// copied in a previes young generation GC or if the semi-space copy above // copied in a previes young generation GC or if the semi-space copy above
// failed. // failed.
result = PromoteObject(map, slot, object, object_size); result = PromoteObject(map, slot, object, object_size, object_fields);
if (result != CopyAndForwardResult::FAILURE) { if (result != CopyAndForwardResult::FAILURE) {
return RememberedSetEntryNeeded(result); return RememberedSetEntryNeeded(result);
} }
// If promotion failed, we try to copy the object to the other semi-space. // If promotion failed, we try to copy the object to the other semi-space.
result = SemiSpaceCopyObject(map, slot, object, object_size); result = SemiSpaceCopyObject(map, slot, object, object_size, object_fields);
if (result != CopyAndForwardResult::FAILURE) { if (result != CopyAndForwardResult::FAILURE) {
return RememberedSetEntryNeeded(result); return RememberedSetEntryNeeded(result);
} }
...@@ -310,7 +292,10 @@ SlotCallbackResult Scavenger::EvacuateThinString(Map map, THeapObjectSlot slot, ...@@ -310,7 +292,10 @@ SlotCallbackResult Scavenger::EvacuateThinString(Map map, THeapObjectSlot slot,
return REMOVE_SLOT; return REMOVE_SLOT;
} }
return EvacuateObjectDefault(map, slot, object, object_size); DCHECK_EQ(ObjectFields::kMaybePointers,
Map::ObjectFieldsFrom(map->visitor_id()));
return EvacuateObjectDefault(map, slot, object, object_size,
ObjectFields::kMaybePointers);
} }
template <typename THeapObjectSlot> template <typename THeapObjectSlot>
...@@ -345,13 +330,16 @@ SlotCallbackResult Scavenger::EvacuateShortcutCandidate(Map map, ...@@ -345,13 +330,16 @@ SlotCallbackResult Scavenger::EvacuateShortcutCandidate(Map map,
} }
Map map = first_word.ToMap(); Map map = first_word.ToMap();
SlotCallbackResult result = SlotCallbackResult result =
EvacuateObjectDefault(map, slot, first, first->SizeFromMap(map)); EvacuateObjectDefault(map, slot, first, first->SizeFromMap(map),
Map::ObjectFieldsFrom(map->visitor_id()));
object->map_slot().Release_Store( object->map_slot().Release_Store(
MapWord::FromForwardingAddress(slot.ToHeapObject()).ToMap()); MapWord::FromForwardingAddress(slot.ToHeapObject()).ToMap());
return result; return result;
} }
DCHECK_EQ(ObjectFields::kMaybePointers,
return EvacuateObjectDefault(map, slot, object, object_size); Map::ObjectFieldsFrom(map->visitor_id()));
return EvacuateObjectDefault(map, slot, object, object_size,
ObjectFields::kMaybePointers);
} }
template <typename THeapObjectSlot> template <typename THeapObjectSlot>
...@@ -365,7 +353,8 @@ SlotCallbackResult Scavenger::EvacuateObject(THeapObjectSlot slot, Map map, ...@@ -365,7 +353,8 @@ SlotCallbackResult Scavenger::EvacuateObject(THeapObjectSlot slot, Map map,
int size = source->SizeFromMap(map); int size = source->SizeFromMap(map);
// Cannot use ::cast() below because that would add checks in debug mode // Cannot use ::cast() below because that would add checks in debug mode
// that require re-reading the map. // that require re-reading the map.
switch (map->visitor_id()) { VisitorId visitor_id = map->visitor_id();
switch (visitor_id) {
case kVisitThinString: case kVisitThinString:
// At the moment we don't allow weak pointers to thin strings. // At the moment we don't allow weak pointers to thin strings.
DCHECK(!(*slot)->IsWeak()); DCHECK(!(*slot)->IsWeak());
...@@ -377,7 +366,8 @@ SlotCallbackResult Scavenger::EvacuateObject(THeapObjectSlot slot, Map map, ...@@ -377,7 +366,8 @@ SlotCallbackResult Scavenger::EvacuateObject(THeapObjectSlot slot, Map map,
return EvacuateShortcutCandidate( return EvacuateShortcutCandidate(
map, slot, ConsString::unchecked_cast(source), size); map, slot, ConsString::unchecked_cast(source), size);
default: default:
return EvacuateObjectDefault(map, slot, source, size); return EvacuateObjectDefault(map, slot, source, size,
Map::ObjectFieldsFrom(visitor_id));
} }
} }
......
...@@ -159,28 +159,28 @@ class Scavenger { ...@@ -159,28 +159,28 @@ class Scavenger {
RememberedSetEntryNeeded(CopyAndForwardResult result); RememberedSetEntryNeeded(CopyAndForwardResult result);
template <typename THeapObjectSlot> template <typename THeapObjectSlot>
V8_INLINE CopyAndForwardResult SemiSpaceCopyObject(Map map, V8_INLINE CopyAndForwardResult
THeapObjectSlot slot, SemiSpaceCopyObject(Map map, THeapObjectSlot slot, HeapObject object,
HeapObject object, int object_size, ObjectFields object_fields);
int object_size);
template <typename THeapObjectSlot> template <typename THeapObjectSlot>
V8_INLINE CopyAndForwardResult PromoteObject(Map map, THeapObjectSlot slot, V8_INLINE CopyAndForwardResult PromoteObject(Map map, THeapObjectSlot slot,
HeapObject object, HeapObject object,
int object_size); int object_size,
ObjectFields object_fields);
template <typename THeapObjectSlot> template <typename THeapObjectSlot>
V8_INLINE SlotCallbackResult EvacuateObject(THeapObjectSlot slot, Map map, V8_INLINE SlotCallbackResult EvacuateObject(THeapObjectSlot slot, Map map,
HeapObject source); HeapObject source);
V8_INLINE bool HandleLargeObject(Map map, HeapObject object, int object_size); V8_INLINE bool HandleLargeObject(Map map, HeapObject object, int object_size,
ObjectFields object_fields);
// Different cases for object evacuation. // Different cases for object evacuation.
template <typename THeapObjectSlot> template <typename THeapObjectSlot>
V8_INLINE SlotCallbackResult EvacuateObjectDefault(Map map, V8_INLINE SlotCallbackResult
THeapObjectSlot slot, EvacuateObjectDefault(Map map, THeapObjectSlot slot, HeapObject object,
HeapObject object, int object_size, ObjectFields object_fields);
int object_size);
template <typename THeapObjectSlot> template <typename THeapObjectSlot>
inline SlotCallbackResult EvacuateThinString(Map map, THeapObjectSlot slot, inline SlotCallbackResult EvacuateThinString(Map map, THeapObjectSlot slot,
...@@ -195,8 +195,6 @@ class Scavenger { ...@@ -195,8 +195,6 @@ class Scavenger {
void IterateAndScavengePromotedObject(HeapObject target, Map map, int size); void IterateAndScavengePromotedObject(HeapObject target, Map map, int size);
static inline bool ContainsOnlyData(VisitorId visitor_id);
ScavengerCollector* const collector_; ScavengerCollector* const collector_;
Heap* const heap_; Heap* const heap_;
PromotionList::View promotion_list_; PromotionList::View promotion_list_;
......
...@@ -18,10 +18,16 @@ namespace internal { ...@@ -18,10 +18,16 @@ namespace internal {
enum InstanceType : uint16_t; enum InstanceType : uint16_t;
#define VISITOR_ID_LIST(V) \ #define DATA_ONLY_VISITOR_ID_LIST(V) \
V(BigInt) \
V(ByteArray) \
V(DataObject) \
V(FixedDoubleArray) \
V(SeqOneByteString) \
V(SeqTwoByteString)
#define POINTER_VISITOR_ID_LIST(V) \
V(AllocationSite) \ V(AllocationSite) \
V(BigInt) \
V(ByteArray) \
V(BytecodeArray) \ V(BytecodeArray) \
V(Cell) \ V(Cell) \
V(Code) \ V(Code) \
...@@ -29,14 +35,12 @@ enum InstanceType : uint16_t; ...@@ -29,14 +35,12 @@ enum InstanceType : uint16_t;
V(ConsString) \ V(ConsString) \
V(Context) \ V(Context) \
V(DataHandler) \ V(DataHandler) \
V(DataObject) \
V(DescriptorArray) \ V(DescriptorArray) \
V(EmbedderDataArray) \ V(EmbedderDataArray) \
V(EphemeronHashTable) \ V(EphemeronHashTable) \
V(FeedbackCell) \ V(FeedbackCell) \
V(FeedbackVector) \ V(FeedbackVector) \
V(FixedArray) \ V(FixedArray) \
V(FixedDoubleArray) \
V(FixedFloat64Array) \ V(FixedFloat64Array) \
V(FixedTypedArrayBase) \ V(FixedTypedArrayBase) \
V(FreeSpace) \ V(FreeSpace) \
...@@ -57,8 +61,6 @@ enum InstanceType : uint16_t; ...@@ -57,8 +61,6 @@ enum InstanceType : uint16_t;
V(PropertyArray) \ V(PropertyArray) \
V(PropertyCell) \ V(PropertyCell) \
V(PrototypeInfo) \ V(PrototypeInfo) \
V(SeqOneByteString) \
V(SeqTwoByteString) \
V(SharedFunctionInfo) \ V(SharedFunctionInfo) \
V(ShortcutCandidate) \ V(ShortcutCandidate) \
V(SlicedString) \ V(SlicedString) \
...@@ -74,22 +76,23 @@ enum InstanceType : uint16_t; ...@@ -74,22 +76,23 @@ enum InstanceType : uint16_t;
V(WasmInstanceObject) \ V(WasmInstanceObject) \
V(WeakArray) V(WeakArray)
// For data objects, JS objects and structs along with generic visitor which // Objects with the same visitor id are processed in the same way by
// can visit object of any size we provide visitors specialized by // the heap visitors. The visitor ids for data only objects must precede
// object size in words. // other visitor ids. We rely on kDataOnlyVisitorIdCount for quick check
// Ids of specialized visitors are declared in a linear order (without // of whether an object contains only data or may contain pointers.
// holes) starting from the id of visitor specialized for 2 words objects
// (base visitor id) and ending with the id of generic visitor.
// Method GetVisitorIdForSize depends on this ordering to calculate visitor
// id of specialized visitor from given instance size, base visitor id and
// generic visitor's id.
enum VisitorId { enum VisitorId {
#define VISITOR_ID_ENUM_DECL(id) kVisit##id, #define VISITOR_ID_ENUM_DECL(id) kVisit##id,
VISITOR_ID_LIST(VISITOR_ID_ENUM_DECL) DATA_ONLY_VISITOR_ID_LIST(VISITOR_ID_ENUM_DECL) kDataOnlyVisitorIdCount,
POINTER_VISITOR_ID_LIST(VISITOR_ID_ENUM_DECL)
#undef VISITOR_ID_ENUM_DECL #undef VISITOR_ID_ENUM_DECL
kVisitorIdCount kVisitorIdCount
}; };
enum class ObjectFields {
kDataOnly,
kMaybePointers,
};
typedef std::vector<Handle<Map>> MapHandles; typedef std::vector<Handle<Map>> MapHandles;
// All heap objects have a Map that describes their structure. // All heap objects have a Map that describes their structure.
...@@ -806,6 +809,12 @@ class Map : public HeapObject { ...@@ -806,6 +809,12 @@ class Map : public HeapObject {
DECL_PRIMITIVE_ACCESSORS(visitor_id, VisitorId) DECL_PRIMITIVE_ACCESSORS(visitor_id, VisitorId)
static ObjectFields ObjectFieldsFrom(VisitorId visitor_id) {
return (visitor_id < kDataOnlyVisitorIdCount)
? ObjectFields::kDataOnly
: ObjectFields::kMaybePointers;
}
static Handle<Map> TransitionToPrototype(Isolate* isolate, Handle<Map> map, static Handle<Map> TransitionToPrototype(Isolate* isolate, Handle<Map> map,
Handle<Object> prototype); Handle<Object> prototype);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment