Commit 8dc72812 authored by ulan@chromium.org's avatar ulan@chromium.org

Start incremental marking on idle notification.

BUG=v8:1458
TEST=cctest/test-api/IdleNotification*

Review URL: http://codereview.chromium.org/8519002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10093 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent e2f2c77e
...@@ -3194,8 +3194,12 @@ class V8EXPORT V8 { ...@@ -3194,8 +3194,12 @@ class V8EXPORT V8 {
* Returns true if the embedder should stop calling IdleNotification * Returns true if the embedder should stop calling IdleNotification
* until real work has been done. This indicates that V8 has done * until real work has been done. This indicates that V8 has done
* as much cleanup as it will be able to do. * as much cleanup as it will be able to do.
*
* The hint argument specifies the amount of work to be done in the function
* on scale from 1 to 1000. There is no guarantee that the actual work will
* match the hint.
*/ */
static bool IdleNotification(); static bool IdleNotification(int hint = 1000);
/** /**
* Optional notification that the system is running low on memory. * Optional notification that the system is running low on memory.
......
...@@ -4020,12 +4020,12 @@ void v8::V8::GetHeapStatistics(HeapStatistics* heap_statistics) { ...@@ -4020,12 +4020,12 @@ void v8::V8::GetHeapStatistics(HeapStatistics* heap_statistics) {
} }
bool v8::V8::IdleNotification() { bool v8::V8::IdleNotification(int hint) {
// Returning true tells the caller that it need not // Returning true tells the caller that it need not
// continue to call IdleNotification. // continue to call IdleNotification.
i::Isolate* isolate = i::Isolate::Current(); i::Isolate* isolate = i::Isolate::Current();
if (isolate == NULL || !isolate->IsInitialized()) return true; if (isolate == NULL || !isolate->IsInitialized()) return true;
return i::V8::IdleNotification(); return i::V8::IdleNotification(hint);
} }
......
...@@ -144,6 +144,11 @@ Heap::Heap() ...@@ -144,6 +144,11 @@ Heap::Heap()
number_idle_notifications_(0), number_idle_notifications_(0),
last_idle_notification_gc_count_(0), last_idle_notification_gc_count_(0),
last_idle_notification_gc_count_init_(false), last_idle_notification_gc_count_init_(false),
idle_notification_will_schedule_next_gc_(false),
mark_sweeps_since_idle_round_started_(0),
ms_count_at_last_idle_notification_(0),
gc_count_at_last_idle_gc_(0),
scavenges_since_last_idle_round_(kIdleScavengeThreshold),
promotion_queue_(this), promotion_queue_(this),
configured_(false), configured_(false),
chunks_queued_for_free_(NULL) { chunks_queued_for_free_(NULL) {
...@@ -1081,8 +1086,7 @@ void Heap::Scavenge() { ...@@ -1081,8 +1086,7 @@ void Heap::Scavenge() {
incremental_marking()->PrepareForScavenge(); incremental_marking()->PrepareForScavenge();
old_pointer_space()->AdvanceSweeper(new_space_.Size()); AdvanceSweepers(new_space_.Size());
old_data_space()->AdvanceSweeper(new_space_.Size());
// Flip the semispaces. After flipping, to space is empty, from space has // Flip the semispaces. After flipping, to space is empty, from space has
// live objects. // live objects.
...@@ -1171,6 +1175,8 @@ void Heap::Scavenge() { ...@@ -1171,6 +1175,8 @@ void Heap::Scavenge() {
LOG(isolate_, ResourceEvent("scavenge", "end")); LOG(isolate_, ResourceEvent("scavenge", "end"));
gc_state_ = NOT_IN_GC; gc_state_ = NOT_IN_GC;
scavenges_since_last_idle_round_++;
} }
...@@ -4534,7 +4540,79 @@ void Heap::EnsureHeapIsIterable() { ...@@ -4534,7 +4540,79 @@ void Heap::EnsureHeapIsIterable() {
} }
bool Heap::IdleNotification() { bool Heap::IdleNotification(int hint) {
if (!FLAG_incremental_marking || FLAG_expose_gc || Serializer::enabled()) {
return hint < 1000 ? true : IdleGlobalGC();
}
// By doing small chunks of GC work in each IdleNotification,
// perform a round of incremental GCs and after that wait until
// the mutator creates enough garbage to justify a new round.
// An incremental GC progresses as follows:
// 1. many incremental marking steps,
// 2. one old space mark-sweep-compact,
// 3. many lazy sweep steps.
// Use mark-sweep-compact events to count incremental GCs in a round.
intptr_t size_factor = Min(Max(hint, 30), 1000) / 10;
// The size factor is in range [3..100].
intptr_t step_size = size_factor * IncrementalMarking::kAllocatedThreshold;
if (incremental_marking()->IsStopped()) {
if (!IsSweepingComplete() && !AdvanceSweepers(step_size)) {
return false;
}
}
if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) {
if (EnoughGarbageSinceLastIdleRound()) {
StartIdleRound();
} else {
return true;
}
}
int new_mark_sweeps = ms_count_ - ms_count_at_last_idle_notification_;
mark_sweeps_since_idle_round_started_ += new_mark_sweeps;
ms_count_at_last_idle_notification_ = ms_count_;
if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) {
FinishIdleRound();
return true;
}
if (incremental_marking()->IsStopped()) {
if (hint < 1000 && !WorthStartingGCWhenIdle()) {
FinishIdleRound();
return true;
}
incremental_marking()->Start();
}
// This flag prevents incremental marking from requesting GC via stack guard
idle_notification_will_schedule_next_gc_ = true;
incremental_marking()->Step(step_size);
idle_notification_will_schedule_next_gc_ = false;
if (incremental_marking()->IsComplete()) {
bool uncommit = false;
if (gc_count_at_last_idle_gc_ == gc_count_) {
// No GC since the last full GC, the mutator is probably not active.
isolate_->compilation_cache()->Clear();
uncommit = true;
}
CollectAllGarbage(kNoGCFlags);
gc_count_at_last_idle_gc_ = gc_count_;
if (uncommit) {
new_space_.Shrink();
UncommitFromSpace();
}
}
return false;
}
bool Heap::IdleGlobalGC() {
static const int kIdlesBeforeScavenge = 4; static const int kIdlesBeforeScavenge = 4;
static const int kIdlesBeforeMarkSweep = 7; static const int kIdlesBeforeMarkSweep = 7;
static const int kIdlesBeforeMarkCompact = 8; static const int kIdlesBeforeMarkCompact = 8;
......
...@@ -1331,8 +1331,8 @@ class Heap { ...@@ -1331,8 +1331,8 @@ class Heap {
return Min(limit, halfway_to_the_max); return Min(limit, halfway_to_the_max);
} }
// Can be called when the embedding application is idle. // Implements the corresponding V8 API function.
bool IdleNotification(); bool IdleNotification(int hint);
// Declare all the root indices. // Declare all the root indices.
enum RootListIndex { enum RootListIndex {
...@@ -1455,6 +1455,17 @@ class Heap { ...@@ -1455,6 +1455,17 @@ class Heap {
return &incremental_marking_; return &incremental_marking_;
} }
bool IsSweepingComplete() {
return old_data_space()->IsSweepingComplete() &&
old_pointer_space()->IsSweepingComplete();
}
bool AdvanceSweepers(int step_size) {
bool sweeping_complete = old_data_space()->AdvanceSweeper(step_size);
sweeping_complete &= old_pointer_space()->AdvanceSweeper(step_size);
return sweeping_complete;
}
ExternalStringTable* external_string_table() { ExternalStringTable* external_string_table() {
return &external_string_table_; return &external_string_table_;
} }
...@@ -1490,6 +1501,10 @@ class Heap { ...@@ -1490,6 +1501,10 @@ class Heap {
// The roots that have an index less than this are always in old space. // The roots that have an index less than this are always in old space.
static const int kOldSpaceRoots = 0x20; static const int kOldSpaceRoots = 0x20;
bool idle_notification_will_schedule_next_gc() {
return idle_notification_will_schedule_next_gc_;
}
private: private:
Heap(); Heap();
...@@ -1823,6 +1838,30 @@ class Heap { ...@@ -1823,6 +1838,30 @@ class Heap {
void SelectScavengingVisitorsTable(); void SelectScavengingVisitorsTable();
void StartIdleRound() {
mark_sweeps_since_idle_round_started_ = 0;
ms_count_at_last_idle_notification_ = ms_count_;
}
void FinishIdleRound() {
mark_sweeps_since_idle_round_started_ = kMaxMarkSweepsInIdleRound;
scavenges_since_last_idle_round_ = 0;
}
bool EnoughGarbageSinceLastIdleRound() {
return (scavenges_since_last_idle_round_ >= kIdleScavengeThreshold);
}
bool WorthStartingGCWhenIdle() {
if (contexts_disposed_ > 0) {
return true;
}
return incremental_marking()->WorthActivating();
}
// Returns true if no more GC work is left.
bool IdleGlobalGC();
static const int kInitialSymbolTableSize = 2048; static const int kInitialSymbolTableSize = 2048;
static const int kInitialEvalCacheSize = 64; static const int kInitialEvalCacheSize = 64;
...@@ -1852,6 +1891,15 @@ class Heap { ...@@ -1852,6 +1891,15 @@ class Heap {
unsigned int last_idle_notification_gc_count_; unsigned int last_idle_notification_gc_count_;
bool last_idle_notification_gc_count_init_; bool last_idle_notification_gc_count_init_;
bool idle_notification_will_schedule_next_gc_;
int mark_sweeps_since_idle_round_started_;
int ms_count_at_last_idle_notification_;
unsigned int gc_count_at_last_idle_gc_;
int scavenges_since_last_idle_round_;
static const int kMaxMarkSweepsInIdleRound = 7;
static const int kIdleScavengeThreshold = 5;
// Shared state read by the scavenge collector and set by ScavengeObject. // Shared state read by the scavenge collector and set by ScavengeObject.
PromotionQueue promotion_queue_; PromotionQueue promotion_queue_;
......
...@@ -743,7 +743,9 @@ void IncrementalMarking::MarkingComplete() { ...@@ -743,7 +743,9 @@ void IncrementalMarking::MarkingComplete() {
if (FLAG_trace_incremental_marking) { if (FLAG_trace_incremental_marking) {
PrintF("[IncrementalMarking] Complete (normal).\n"); PrintF("[IncrementalMarking] Complete (normal).\n");
} }
heap_->isolate()->stack_guard()->RequestGC(); if (!heap_->idle_notification_will_schedule_next_gc()) {
heap_->isolate()->stack_guard()->RequestGC();
}
} }
...@@ -771,8 +773,7 @@ void IncrementalMarking::Step(intptr_t allocated_bytes) { ...@@ -771,8 +773,7 @@ void IncrementalMarking::Step(intptr_t allocated_bytes) {
} }
if (state_ == SWEEPING) { if (state_ == SWEEPING) {
if (heap_->old_pointer_space()->AdvanceSweeper(bytes_to_process) && if (heap_->AdvanceSweepers(bytes_to_process)) {
heap_->old_data_space()->AdvanceSweeper(bytes_to_process)) {
bytes_scanned_ = 0; bytes_scanned_ = 0;
StartMarking(PREVENT_COMPACTION); StartMarking(PREVENT_COMPACTION);
} }
......
...@@ -63,6 +63,8 @@ class IncrementalMarking { ...@@ -63,6 +63,8 @@ class IncrementalMarking {
inline bool IsMarkingIncomplete() { return state() == MARKING; } inline bool IsMarkingIncomplete() { return state() == MARKING; }
inline bool IsComplete() { return state() == COMPLETE; }
bool WorthActivating(); bool WorthActivating();
void Start(); void Start();
...@@ -101,6 +103,7 @@ class IncrementalMarking { ...@@ -101,6 +103,7 @@ class IncrementalMarking {
void OldSpaceStep(intptr_t allocated) { void OldSpaceStep(intptr_t allocated) {
Step(allocated * kFastMarking / kInitialAllocationMarkingFactor); Step(allocated * kFastMarking / kInitialAllocationMarkingFactor);
} }
void Step(intptr_t allocated); void Step(intptr_t allocated);
inline void RestartIfNotMarking() { inline void RestartIfNotMarking() {
......
...@@ -166,13 +166,13 @@ uint32_t V8::RandomPrivate(Isolate* isolate) { ...@@ -166,13 +166,13 @@ uint32_t V8::RandomPrivate(Isolate* isolate) {
} }
bool V8::IdleNotification() { bool V8::IdleNotification(int hint) {
// Returning true tells the caller that there is no need to call // Returning true tells the caller that there is no need to call
// IdleNotification again. // IdleNotification again.
if (!FLAG_use_idle_notification) return true; if (!FLAG_use_idle_notification) return true;
// Tell the heap that it may want to adjust. // Tell the heap that it may want to adjust.
return HEAP->IdleNotification(); return HEAP->IdleNotification(hint);
} }
......
...@@ -106,7 +106,7 @@ class V8 : public AllStatic { ...@@ -106,7 +106,7 @@ class V8 : public AllStatic {
Context* context); Context* context);
// Idle notification directly from the API. // Idle notification directly from the API.
static bool IdleNotification(); static bool IdleNotification(int hint);
private: private:
static void InitializeOncePerProcess(); static void InitializeOncePerProcess();
......
...@@ -13439,13 +13439,60 @@ TEST(SourceURLInStackTrace) { ...@@ -13439,13 +13439,60 @@ TEST(SourceURLInStackTrace) {
// Test that idle notification can be handled and eventually returns true. // Test that idle notification can be handled and eventually returns true.
THREADED_TEST(IdleNotification) { THREADED_TEST(IdleNotification) {
v8::HandleScope scope;
LocalContext env;
CompileRun("function binom(n, m) {"
" var C = [[1]];"
" for (var i = 1; i <= n; ++i) {"
" C[i] = [1];"
" for (var j = 1; j < i; ++j) {"
" C[i][j] = C[i-1][j-1] + C[i-1][j];"
" }"
" C[i][i] = 1;"
" }"
" return C[n][m];"
"};"
"binom(1000, 500)");
bool rv = false; bool rv = false;
intptr_t old_size = HEAP->SizeOfObjects();
bool no_idle_work = v8::V8::IdleNotification();
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {
rv = v8::V8::IdleNotification(); rv = v8::V8::IdleNotification();
if (rv) if (rv)
break; break;
} }
CHECK(rv == true); CHECK(rv == true);
intptr_t new_size = HEAP->SizeOfObjects();
CHECK(no_idle_work || new_size < 3 * old_size / 4);
}
// Test that idle notification can be handled and eventually returns true.
THREADED_TEST(IdleNotificationWithHint) {
v8::HandleScope scope;
LocalContext env;
CompileRun("function binom(n, m) {"
" var C = [[1]];"
" for (var i = 1; i <= n; ++i) {"
" C[i] = [1];"
" for (var j = 1; j < i; ++j) {"
" C[i][j] = C[i-1][j-1] + C[i-1][j];"
" }"
" C[i][i] = 1;"
" }"
" return C[n][m];"
"};"
"binom(1000, 500)");
bool rv = false;
intptr_t old_size = HEAP->SizeOfObjects();
bool no_idle_work = v8::V8::IdleNotification(10);
for (int i = 0; i < 200; i++) {
rv = v8::V8::IdleNotification(10);
if (rv)
break;
}
CHECK(rv == true);
intptr_t new_size = HEAP->SizeOfObjects();
CHECK(no_idle_work || new_size < 3 * old_size / 4);
} }
......
...@@ -1289,3 +1289,31 @@ TEST(CollectingAllAvailableGarbageShrinksNewSpace) { ...@@ -1289,3 +1289,31 @@ TEST(CollectingAllAvailableGarbageShrinksNewSpace) {
new_capacity = new_space->Capacity(); new_capacity = new_space->Capacity();
CHECK(old_capacity == new_capacity); CHECK(old_capacity == new_capacity);
} }
TEST(IdleNotificationAdvancesIncrementalMarking) {
if (!FLAG_incremental_marking || !FLAG_incremental_marking_steps) return;
InitializeVM();
v8::HandleScope scope;
const char* source = "function binom(n, m) {"
" var C = [[1]];"
" for (var i = 1; i <= n; ++i) {"
" C[i] = [1];"
" for (var j = 1; j < i; ++j) {"
" C[i][j] = C[i-1][j-1] + C[i-1][j];"
" }"
" C[i][i] = 1;"
" }"
" return C[n][m];"
"};"
"binom(1000, 500)";
{
AlwaysAllocateScope aa_scope;
CompileRun(source);
}
intptr_t old_size = HEAP->SizeOfObjects();
bool no_idle_work = v8::V8::IdleNotification();
while (!v8::V8::IdleNotification());
intptr_t new_size = HEAP->SizeOfObjects();
CHECK(no_idle_work || new_size < 3 * old_size / 4);
}
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