Implement progress bar for large objects.
This implements incremental scanning of large objects using a progress bar in the page header of such objects. Note that this requires forward white to gray transitions in the write barrier and hence is disabled by default for now. R=ulan@chromium.org,hpayer@chromium.org Review URL: https://codereview.chromium.org/11362246 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12978 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
4ce6de208d
commit
5dec1423a4
@ -438,6 +438,9 @@ DEFINE_bool(incremental_code_compaction, true,
|
|||||||
DEFINE_bool(cleanup_code_caches_at_gc, true,
|
DEFINE_bool(cleanup_code_caches_at_gc, true,
|
||||||
"Flush inline caches prior to mark compact collection and "
|
"Flush inline caches prior to mark compact collection and "
|
||||||
"flush code caches in maps during mark compact cycle.")
|
"flush code caches in maps during mark compact cycle.")
|
||||||
|
DEFINE_bool(use_marking_progress_bar, false,
|
||||||
|
"Use a progress bar to scan large objects in increments when "
|
||||||
|
"incremental marking is active.")
|
||||||
DEFINE_int(random_seed, 0,
|
DEFINE_int(random_seed, 0,
|
||||||
"Default seed for initializing random generator "
|
"Default seed for initializing random generator "
|
||||||
"(0, the default, means to use system random).")
|
"(0, the default, means to use system random).")
|
||||||
|
@ -188,16 +188,78 @@ static void MarkObjectGreyDoNotEnqueue(Object* obj) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void MarkBlackOrKeepGrey(HeapObject* heap_object,
|
||||||
|
MarkBit mark_bit,
|
||||||
|
int size) {
|
||||||
|
ASSERT(!Marking::IsImpossible(mark_bit));
|
||||||
|
if (mark_bit.Get()) return;
|
||||||
|
mark_bit.Set();
|
||||||
|
MemoryChunk::IncrementLiveBytesFromGC(heap_object->address(), size);
|
||||||
|
ASSERT(Marking::IsBlack(mark_bit));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void MarkBlackOrKeepBlack(HeapObject* heap_object,
|
||||||
|
MarkBit mark_bit,
|
||||||
|
int size) {
|
||||||
|
ASSERT(!Marking::IsImpossible(mark_bit));
|
||||||
|
if (Marking::IsBlack(mark_bit)) return;
|
||||||
|
Marking::MarkBlack(mark_bit);
|
||||||
|
MemoryChunk::IncrementLiveBytesFromGC(heap_object->address(), size);
|
||||||
|
ASSERT(Marking::IsBlack(mark_bit));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class IncrementalMarkingMarkingVisitor
|
class IncrementalMarkingMarkingVisitor
|
||||||
: public StaticMarkingVisitor<IncrementalMarkingMarkingVisitor> {
|
: public StaticMarkingVisitor<IncrementalMarkingMarkingVisitor> {
|
||||||
public:
|
public:
|
||||||
static void Initialize() {
|
static void Initialize() {
|
||||||
StaticMarkingVisitor<IncrementalMarkingMarkingVisitor>::Initialize();
|
StaticMarkingVisitor<IncrementalMarkingMarkingVisitor>::Initialize();
|
||||||
|
table_.Register(kVisitFixedArray, &VisitFixedArrayIncremental);
|
||||||
table_.Register(kVisitNativeContext, &VisitNativeContextIncremental);
|
table_.Register(kVisitNativeContext, &VisitNativeContextIncremental);
|
||||||
table_.Register(kVisitJSRegExp, &VisitJSRegExp);
|
table_.Register(kVisitJSRegExp, &VisitJSRegExp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const int kProgressBarScanningChunk = 32 * 1024;
|
||||||
|
|
||||||
|
static void VisitFixedArrayIncremental(Map* map, HeapObject* object) {
|
||||||
|
MemoryChunk* chunk = MemoryChunk::FromAddress(object->address());
|
||||||
|
// TODO(mstarzinger): Move setting of the flag to the allocation site of
|
||||||
|
// the array. The visitor should just check the flag.
|
||||||
|
if (FLAG_use_marking_progress_bar &&
|
||||||
|
chunk->owner()->identity() == LO_SPACE) {
|
||||||
|
chunk->SetFlag(MemoryChunk::HAS_PROGRESS_BAR);
|
||||||
|
}
|
||||||
|
if (chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR)) {
|
||||||
|
Heap* heap = map->GetHeap();
|
||||||
|
// When using a progress bar for large fixed arrays, scan only a chunk of
|
||||||
|
// the array and try to push it onto the marking deque again until it is
|
||||||
|
// fully scanned. Fall back to scanning it through to the end in case this
|
||||||
|
// fails because of a full deque.
|
||||||
|
int object_size = FixedArray::BodyDescriptor::SizeOf(map, object);
|
||||||
|
int start_offset = Max(FixedArray::BodyDescriptor::kStartOffset,
|
||||||
|
chunk->progress_bar());
|
||||||
|
int end_offset = Min(object_size,
|
||||||
|
start_offset + kProgressBarScanningChunk);
|
||||||
|
bool scan_until_end = false;
|
||||||
|
do {
|
||||||
|
VisitPointersWithAnchor(heap,
|
||||||
|
HeapObject::RawField(object, 0),
|
||||||
|
HeapObject::RawField(object, start_offset),
|
||||||
|
HeapObject::RawField(object, end_offset));
|
||||||
|
start_offset = end_offset;
|
||||||
|
end_offset = Min(object_size, end_offset + kProgressBarScanningChunk);
|
||||||
|
scan_until_end = heap->incremental_marking()->marking_deque()->IsFull();
|
||||||
|
} while (scan_until_end && start_offset < object_size);
|
||||||
|
chunk->set_progress_bar(start_offset);
|
||||||
|
if (start_offset < object_size) {
|
||||||
|
heap->incremental_marking()->marking_deque()->UnshiftGrey(object);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FixedArrayVisitor::Visit(map, object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void VisitNativeContextIncremental(Map* map, HeapObject* object) {
|
static void VisitNativeContextIncremental(Map* map, HeapObject* object) {
|
||||||
Context* context = Context::cast(object);
|
Context* context = Context::cast(object);
|
||||||
|
|
||||||
@ -234,15 +296,25 @@ class IncrementalMarkingMarkingVisitor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INLINE(static void VisitPointersWithAnchor(Heap* heap,
|
||||||
|
Object** anchor,
|
||||||
|
Object** start,
|
||||||
|
Object** end)) {
|
||||||
|
for (Object** p = start; p < end; p++) {
|
||||||
|
Object* obj = *p;
|
||||||
|
if (obj->NonFailureIsHeapObject()) {
|
||||||
|
heap->mark_compact_collector()->RecordSlot(anchor, p, obj);
|
||||||
|
MarkObject(heap, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Marks the object grey and pushes it on the marking stack.
|
// Marks the object grey and pushes it on the marking stack.
|
||||||
INLINE(static void MarkObject(Heap* heap, Object* obj)) {
|
INLINE(static void MarkObject(Heap* heap, Object* obj)) {
|
||||||
HeapObject* heap_object = HeapObject::cast(obj);
|
HeapObject* heap_object = HeapObject::cast(obj);
|
||||||
MarkBit mark_bit = Marking::MarkBitFrom(heap_object);
|
MarkBit mark_bit = Marking::MarkBitFrom(heap_object);
|
||||||
if (mark_bit.data_only()) {
|
if (mark_bit.data_only()) {
|
||||||
if (heap->incremental_marking()->MarkBlackOrKeepGrey(mark_bit)) {
|
MarkBlackOrKeepGrey(heap_object, mark_bit, heap_object->Size());
|
||||||
MemoryChunk::IncrementLiveBytesFromGC(heap_object->address(),
|
|
||||||
heap_object->Size());
|
|
||||||
}
|
|
||||||
} else if (Marking::IsWhite(mark_bit)) {
|
} else if (Marking::IsWhite(mark_bit)) {
|
||||||
heap->incremental_marking()->WhiteToGreyAndPush(heap_object, mark_bit);
|
heap->incremental_marking()->WhiteToGreyAndPush(heap_object, mark_bit);
|
||||||
}
|
}
|
||||||
@ -288,10 +360,7 @@ class IncrementalMarkingRootMarkingVisitor : public ObjectVisitor {
|
|||||||
HeapObject* heap_object = HeapObject::cast(obj);
|
HeapObject* heap_object = HeapObject::cast(obj);
|
||||||
MarkBit mark_bit = Marking::MarkBitFrom(heap_object);
|
MarkBit mark_bit = Marking::MarkBitFrom(heap_object);
|
||||||
if (mark_bit.data_only()) {
|
if (mark_bit.data_only()) {
|
||||||
if (incremental_marking_->MarkBlackOrKeepGrey(mark_bit)) {
|
MarkBlackOrKeepGrey(heap_object, mark_bit, heap_object->Size());
|
||||||
MemoryChunk::IncrementLiveBytesFromGC(heap_object->address(),
|
|
||||||
heap_object->Size());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (Marking::IsWhite(mark_bit)) {
|
if (Marking::IsWhite(mark_bit)) {
|
||||||
incremental_marking_->WhiteToGreyAndPush(heap_object, mark_bit);
|
incremental_marking_->WhiteToGreyAndPush(heap_object, mark_bit);
|
||||||
@ -616,8 +685,11 @@ void IncrementalMarking::UpdateMarkingDequeAfterScavenge() {
|
|||||||
ASSERT(new_top != marking_deque_.bottom());
|
ASSERT(new_top != marking_deque_.bottom());
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
MarkBit mark_bit = Marking::MarkBitFrom(obj);
|
MarkBit mark_bit = Marking::MarkBitFrom(obj);
|
||||||
|
MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
|
||||||
ASSERT(Marking::IsGrey(mark_bit) ||
|
ASSERT(Marking::IsGrey(mark_bit) ||
|
||||||
(obj->IsFiller() && Marking::IsWhite(mark_bit)));
|
(obj->IsFiller() && Marking::IsWhite(mark_bit)) ||
|
||||||
|
(chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR) &&
|
||||||
|
Marking::IsBlack(mark_bit)));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -637,11 +709,15 @@ void IncrementalMarking::VisitObject(Map* map, HeapObject* obj, int size) {
|
|||||||
|
|
||||||
IncrementalMarkingMarkingVisitor::IterateBody(map, obj);
|
IncrementalMarkingMarkingVisitor::IterateBody(map, obj);
|
||||||
|
|
||||||
MarkBit obj_mark_bit = Marking::MarkBitFrom(obj);
|
MarkBit mark_bit = Marking::MarkBitFrom(obj);
|
||||||
SLOW_ASSERT(Marking::IsGrey(obj_mark_bit) ||
|
#ifdef DEBUG
|
||||||
(obj->IsFiller() && Marking::IsWhite(obj_mark_bit)));
|
MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
|
||||||
Marking::MarkBlack(obj_mark_bit);
|
SLOW_ASSERT(Marking::IsGrey(mark_bit) ||
|
||||||
MemoryChunk::IncrementLiveBytesFromGC(obj->address(), size);
|
(obj->IsFiller() && Marking::IsWhite(mark_bit)) ||
|
||||||
|
(chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR) &&
|
||||||
|
Marking::IsBlack(mark_bit)));
|
||||||
|
#endif
|
||||||
|
MarkBlackOrKeepBlack(obj, mark_bit, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -164,19 +164,6 @@ class IncrementalMarking {
|
|||||||
|
|
||||||
inline void WhiteToGreyAndPush(HeapObject* obj, MarkBit mark_bit);
|
inline void WhiteToGreyAndPush(HeapObject* obj, MarkBit mark_bit);
|
||||||
|
|
||||||
// Does white->black or keeps gray or black color. Returns true if converting
|
|
||||||
// white to black.
|
|
||||||
inline bool MarkBlackOrKeepGrey(MarkBit mark_bit) {
|
|
||||||
ASSERT(!Marking::IsImpossible(mark_bit));
|
|
||||||
if (mark_bit.Get()) {
|
|
||||||
// Grey or black: Keep the color.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
mark_bit.Set();
|
|
||||||
ASSERT(Marking::IsBlack(mark_bit));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int steps_count() {
|
inline int steps_count() {
|
||||||
return steps_count_;
|
return steps_count_;
|
||||||
}
|
}
|
||||||
|
@ -488,6 +488,7 @@ void MarkCompactCollector::ClearMarkbits() {
|
|||||||
MarkBit mark_bit = Marking::MarkBitFrom(obj);
|
MarkBit mark_bit = Marking::MarkBitFrom(obj);
|
||||||
mark_bit.Clear();
|
mark_bit.Clear();
|
||||||
mark_bit.Next().Clear();
|
mark_bit.Next().Clear();
|
||||||
|
Page::FromAddress(obj->address())->ResetProgressBar();
|
||||||
Page::FromAddress(obj->address())->ResetLiveBytes();
|
Page::FromAddress(obj->address())->ResetLiveBytes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,10 +110,7 @@ void StaticMarkingVisitor<StaticVisitor>::Initialize() {
|
|||||||
SlicedString::BodyDescriptor,
|
SlicedString::BodyDescriptor,
|
||||||
void>::Visit);
|
void>::Visit);
|
||||||
|
|
||||||
table_.Register(kVisitFixedArray,
|
table_.Register(kVisitFixedArray, &FixedArrayVisitor::Visit);
|
||||||
&FlexibleBodyVisitor<StaticVisitor,
|
|
||||||
FixedArray::BodyDescriptor,
|
|
||||||
void>::Visit);
|
|
||||||
|
|
||||||
table_.Register(kVisitFixedDoubleArray, &DataObjectVisitor::Visit);
|
table_.Register(kVisitFixedDoubleArray, &DataObjectVisitor::Visit);
|
||||||
|
|
||||||
|
@ -434,6 +434,10 @@ class StaticMarkingVisitor : public StaticVisitorBase {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef FlexibleBodyVisitor<StaticVisitor,
|
||||||
|
FixedArray::BodyDescriptor,
|
||||||
|
void> FixedArrayVisitor;
|
||||||
|
|
||||||
typedef FlexibleBodyVisitor<StaticVisitor,
|
typedef FlexibleBodyVisitor<StaticVisitor,
|
||||||
JSObject::BodyDescriptor,
|
JSObject::BodyDescriptor,
|
||||||
void> JSObjectVisitor;
|
void> JSObjectVisitor;
|
||||||
|
@ -448,6 +448,7 @@ MemoryChunk* MemoryChunk::Initialize(Heap* heap,
|
|||||||
chunk->slots_buffer_ = NULL;
|
chunk->slots_buffer_ = NULL;
|
||||||
chunk->skip_list_ = NULL;
|
chunk->skip_list_ = NULL;
|
||||||
chunk->write_barrier_counter_ = kWriteBarrierCounterGranularity;
|
chunk->write_barrier_counter_ = kWriteBarrierCounterGranularity;
|
||||||
|
chunk->progress_bar_ = 0;
|
||||||
chunk->high_water_mark_ = static_cast<int>(area_start - base);
|
chunk->high_water_mark_ = static_cast<int>(area_start - base);
|
||||||
chunk->ResetLiveBytes();
|
chunk->ResetLiveBytes();
|
||||||
Bitmap::Clear(chunk);
|
Bitmap::Clear(chunk);
|
||||||
@ -2784,7 +2785,8 @@ void LargeObjectSpace::FreeUnmarkedObjects() {
|
|||||||
MarkBit mark_bit = Marking::MarkBitFrom(object);
|
MarkBit mark_bit = Marking::MarkBitFrom(object);
|
||||||
if (mark_bit.Get()) {
|
if (mark_bit.Get()) {
|
||||||
mark_bit.Clear();
|
mark_bit.Clear();
|
||||||
MemoryChunk::IncrementLiveBytesFromGC(object->address(), -object->Size());
|
Page::FromAddress(object->address())->ResetProgressBar();
|
||||||
|
Page::FromAddress(object->address())->ResetLiveBytes();
|
||||||
previous = current;
|
previous = current;
|
||||||
current = current->next_page();
|
current = current->next_page();
|
||||||
} else {
|
} else {
|
||||||
|
28
src/spaces.h
28
src/spaces.h
@ -397,6 +397,12 @@ class MemoryChunk {
|
|||||||
WAS_SWEPT_PRECISELY,
|
WAS_SWEPT_PRECISELY,
|
||||||
WAS_SWEPT_CONSERVATIVELY,
|
WAS_SWEPT_CONSERVATIVELY,
|
||||||
|
|
||||||
|
// Large objects can have a progress bar in their page header. These object
|
||||||
|
// are scanned in increments and will be kept black while being scanned.
|
||||||
|
// Even if the mutator writes to them they will be kept black and a white
|
||||||
|
// to grey transition is performed in the value.
|
||||||
|
HAS_PROGRESS_BAR,
|
||||||
|
|
||||||
// Last flag, keep at bottom.
|
// Last flag, keep at bottom.
|
||||||
NUM_MEMORY_CHUNK_FLAGS
|
NUM_MEMORY_CHUNK_FLAGS
|
||||||
};
|
};
|
||||||
@ -480,6 +486,23 @@ class MemoryChunk {
|
|||||||
write_barrier_counter_ = counter;
|
write_barrier_counter_ = counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int progress_bar() {
|
||||||
|
ASSERT(IsFlagSet(HAS_PROGRESS_BAR));
|
||||||
|
return progress_bar_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_progress_bar(int progress_bar) {
|
||||||
|
ASSERT(IsFlagSet(HAS_PROGRESS_BAR));
|
||||||
|
progress_bar_ = progress_bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResetProgressBar() {
|
||||||
|
if (IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR)) {
|
||||||
|
set_progress_bar(0);
|
||||||
|
ClearFlag(MemoryChunk::HAS_PROGRESS_BAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void IncrementLiveBytesFromGC(Address address, int by) {
|
static void IncrementLiveBytesFromGC(Address address, int by) {
|
||||||
MemoryChunk::FromAddress(address)->IncrementLiveBytes(by);
|
MemoryChunk::FromAddress(address)->IncrementLiveBytes(by);
|
||||||
@ -505,7 +528,7 @@ class MemoryChunk {
|
|||||||
kSlotsBufferOffset + kPointerSize + kPointerSize;
|
kSlotsBufferOffset + kPointerSize + kPointerSize;
|
||||||
|
|
||||||
static const size_t kHeaderSize =
|
static const size_t kHeaderSize =
|
||||||
kWriteBarrierCounterOffset + kPointerSize + kPointerSize;
|
kWriteBarrierCounterOffset + kPointerSize + kIntSize + kIntSize;
|
||||||
|
|
||||||
static const int kBodyOffset =
|
static const int kBodyOffset =
|
||||||
CODE_POINTER_ALIGN(kHeaderSize + Bitmap::kSize);
|
CODE_POINTER_ALIGN(kHeaderSize + Bitmap::kSize);
|
||||||
@ -649,6 +672,9 @@ class MemoryChunk {
|
|||||||
SlotsBuffer* slots_buffer_;
|
SlotsBuffer* slots_buffer_;
|
||||||
SkipList* skip_list_;
|
SkipList* skip_list_;
|
||||||
intptr_t write_barrier_counter_;
|
intptr_t write_barrier_counter_;
|
||||||
|
// Used by the incremental marker to keep track of the scanning progress in
|
||||||
|
// large objects that have a progress bar and are scanned in increments.
|
||||||
|
int progress_bar_;
|
||||||
// Assuming the initial allocation on a page is sequential,
|
// Assuming the initial allocation on a page is sequential,
|
||||||
// count highest number of bytes ever allocated on the page.
|
// count highest number of bytes ever allocated on the page.
|
||||||
int high_water_mark_;
|
int high_water_mark_;
|
||||||
|
Loading…
Reference in New Issue
Block a user