Commit 53fdf75b authored by ulan@chromium.org's avatar ulan@chromium.org

Move idle notification handling to GCIdleTimeHandler.

BUG=
R=hpayer@chromium.org

Review URL: https://codereview.chromium.org/492763002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23282 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 758dd167
...@@ -3,12 +3,14 @@ ...@@ -3,12 +3,14 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "src/heap/gc-idle-time-handler.h" #include "src/heap/gc-idle-time-handler.h"
#include "src/heap/gc-tracer.h"
#include "src/utils.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
const double GCIdleTimeHandler::kConservativeTimeRatio = 0.9; const double GCIdleTimeHandler::kConservativeTimeRatio = 0.9;
const size_t GCIdleTimeHandler::kMaxMarkCompactTimeInMs = 1000000;
size_t GCIdleTimeHandler::EstimateMarkingStepSize( size_t GCIdleTimeHandler::EstimateMarkingStepSize(
...@@ -16,14 +18,13 @@ size_t GCIdleTimeHandler::EstimateMarkingStepSize( ...@@ -16,14 +18,13 @@ size_t GCIdleTimeHandler::EstimateMarkingStepSize(
DCHECK(idle_time_in_ms > 0); DCHECK(idle_time_in_ms > 0);
if (marking_speed_in_bytes_per_ms == 0) { if (marking_speed_in_bytes_per_ms == 0) {
marking_speed_in_bytes_per_ms = marking_speed_in_bytes_per_ms = kInitialConservativeMarkingSpeed;
GCIdleTimeHandler::kInitialConservativeMarkingSpeed;
} }
size_t marking_step_size = marking_speed_in_bytes_per_ms * idle_time_in_ms; size_t marking_step_size = marking_speed_in_bytes_per_ms * idle_time_in_ms;
if (marking_step_size / marking_speed_in_bytes_per_ms != idle_time_in_ms) { if (marking_step_size / marking_speed_in_bytes_per_ms != idle_time_in_ms) {
// In the case of an overflow we return maximum marking step size. // In the case of an overflow we return maximum marking step size.
return GCIdleTimeHandler::kMaximumMarkingStepSize; return kMaximumMarkingStepSize;
} }
if (marking_step_size > kMaximumMarkingStepSize) if (marking_step_size > kMaximumMarkingStepSize)
...@@ -32,5 +33,51 @@ size_t GCIdleTimeHandler::EstimateMarkingStepSize( ...@@ -32,5 +33,51 @@ size_t GCIdleTimeHandler::EstimateMarkingStepSize(
return static_cast<size_t>(marking_step_size * return static_cast<size_t>(marking_step_size *
GCIdleTimeHandler::kConservativeTimeRatio); GCIdleTimeHandler::kConservativeTimeRatio);
} }
size_t GCIdleTimeHandler::EstimateMarkCompactTime(
size_t size_of_objects, size_t mark_compact_speed_in_bytes_per_ms) {
if (mark_compact_speed_in_bytes_per_ms == 0) {
mark_compact_speed_in_bytes_per_ms = kInitialConservativeMarkCompactSpeed;
}
size_t result = size_of_objects / mark_compact_speed_in_bytes_per_ms;
return Min(result, kMaxMarkCompactTimeInMs);
}
GCIdleTimeAction GCIdleTimeHandler::Compute(int idle_time_in_ms,
int contexts_disposed,
size_t size_of_objects,
bool incremental_marking_stopped,
GCTracer* gc_tracer) {
if (IsIdleRoundFinished()) {
if (EnoughGarbageSinceLastIdleRound() || contexts_disposed > 0) {
StartIdleRound();
} else {
return GCIdleTimeAction::Nothing();
}
}
if (incremental_marking_stopped) {
size_t speed =
static_cast<size_t>(gc_tracer->MarkCompactSpeedInBytesPerMillisecond());
if (idle_time_in_ms >=
static_cast<int>(EstimateMarkCompactTime(size_of_objects, speed))) {
// If there are no more than two GCs left in this idle round and we are
// allowed to do a full GC, then make those GCs full in order to compact
// the code space.
// TODO(ulan): Once we enable code compaction for incremental marking, we
// can get rid of this special case and always start incremental marking.
int remaining_mark_sweeps =
kMaxMarkCompactsInIdleRound - mark_compacts_since_idle_round_started_;
if (contexts_disposed > 0 || remaining_mark_sweeps <= 2) {
return GCIdleTimeAction::FullGC();
}
}
}
intptr_t speed = gc_tracer->IncrementalMarkingSpeedInBytesPerMillisecond();
size_t step_size =
static_cast<size_t>(EstimateMarkingStepSize(idle_time_in_ms, speed));
return GCIdleTimeAction::IncrementalMarking(step_size);
}
} }
} }
...@@ -10,13 +10,51 @@ ...@@ -10,13 +10,51 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
enum GCIdleTimeActionType {
DO_NOTHING,
DO_INCREMENTAL_MARKING,
DO_SCAVENGE,
DO_FULL_GC
};
class GCIdleTimeAction {
public:
static GCIdleTimeAction Nothing() {
GCIdleTimeAction result;
result.type = DO_NOTHING;
result.parameter = 0;
return result;
}
static GCIdleTimeAction IncrementalMarking(intptr_t step_size) {
GCIdleTimeAction result;
result.type = DO_INCREMENTAL_MARKING;
result.parameter = step_size;
return result;
}
static GCIdleTimeAction Scavenge() {
GCIdleTimeAction result;
result.type = DO_SCAVENGE;
result.parameter = 0;
return result;
}
static GCIdleTimeAction FullGC() {
GCIdleTimeAction result;
result.type = DO_FULL_GC;
result.parameter = 0;
return result;
}
GCIdleTimeActionType type;
intptr_t parameter;
};
class GCTracer;
// The idle time handler makes decisions about which garbage collection // The idle time handler makes decisions about which garbage collection
// operations are executing during IdleNotification. // operations are executing during IdleNotification.
class GCIdleTimeHandler { class GCIdleTimeHandler {
public: public:
static size_t EstimateMarkingStepSize(size_t idle_time_in_ms,
size_t marking_speed_in_bytes_per_ms);
// If we haven't recorded any incremental marking events yet, we carefully // If we haven't recorded any incremental marking events yet, we carefully
// mark with a conservative lower bound for the marking speed. // mark with a conservative lower bound for the marking speed.
static const size_t kInitialConservativeMarkingSpeed = 100 * KB; static const size_t kInitialConservativeMarkingSpeed = 100 * KB;
...@@ -28,7 +66,55 @@ class GCIdleTimeHandler { ...@@ -28,7 +66,55 @@ class GCIdleTimeHandler {
// idle_time_in_ms. Hence, we conservatively prune our workload estimate. // idle_time_in_ms. Hence, we conservatively prune our workload estimate.
static const double kConservativeTimeRatio; static const double kConservativeTimeRatio;
// If we haven't recorded any mark-compact events yet, we use
// conservative lower bound for the mark-compact speed.
static const size_t kInitialConservativeMarkCompactSpeed = 2 * MB;
// Maximum mark-compact time returned by EstimateMarkCompactTime.
static const size_t kMaxMarkCompactTimeInMs;
GCIdleTimeHandler()
: mark_compacts_since_idle_round_started_(0),
scavenges_since_last_idle_round_(0) {}
GCIdleTimeAction Compute(int idle_time_in_ms, int contexts_disposed,
size_t size_of_objects,
bool incremental_marking_stopped,
GCTracer* gc_tracer);
void NotifyIdleMarkCompact() {
if (mark_compacts_since_idle_round_started_ < kMaxMarkCompactsInIdleRound) {
++mark_compacts_since_idle_round_started_;
if (mark_compacts_since_idle_round_started_ ==
kMaxMarkCompactsInIdleRound) {
scavenges_since_last_idle_round_ = 0;
}
}
}
void NotifyScavenge() { ++scavenges_since_last_idle_round_; }
static size_t EstimateMarkingStepSize(size_t idle_time_in_ms,
size_t marking_speed_in_bytes_per_ms);
static size_t EstimateMarkCompactTime(
size_t size_of_objects, size_t mark_compact_speed_in_bytes_per_ms);
private: private:
void StartIdleRound() { mark_compacts_since_idle_round_started_ = 0; }
bool IsIdleRoundFinished() {
return mark_compacts_since_idle_round_started_ ==
kMaxMarkCompactsInIdleRound;
}
bool EnoughGarbageSinceLastIdleRound() {
return scavenges_since_last_idle_round_ >= kIdleScavengeThreshold;
}
static const int kMaxMarkCompactsInIdleRound = 7;
static const int kIdleScavengeThreshold = 5;
int mark_compacts_since_idle_round_started_;
int scavenges_since_last_idle_round_;
DISALLOW_COPY_AND_ASSIGN(GCIdleTimeHandler); DISALLOW_COPY_AND_ASSIGN(GCIdleTimeHandler);
}; };
......
...@@ -418,5 +418,22 @@ intptr_t GCTracer::ScavengeSpeedInBytesPerMillisecond() const { ...@@ -418,5 +418,22 @@ intptr_t GCTracer::ScavengeSpeedInBytesPerMillisecond() const {
return static_cast<intptr_t>(bytes / durations); return static_cast<intptr_t>(bytes / durations);
} }
intptr_t GCTracer::MarkCompactSpeedInBytesPerMillisecond() const {
intptr_t bytes = 0;
double durations = 0.0;
EventBuffer::const_iterator iter = mark_compactor_events_.begin();
while (iter != mark_compactor_events_.end()) {
bytes += iter->start_object_size;
durations += iter->end_time - iter->start_time +
iter->pure_incremental_marking_duration;
++iter;
}
if (durations == 0.0) return 0;
return static_cast<intptr_t>(bytes / durations);
}
} }
} // namespace v8::internal } // namespace v8::internal
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef V8_HEAP_GC_TRACER_H_ #ifndef V8_HEAP_GC_TRACER_H_
#define V8_HEAP_GC_TRACER_H_ #define V8_HEAP_GC_TRACER_H_
#include "src/base/platform/platform.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -81,9 +83,9 @@ class RingBuffer { ...@@ -81,9 +83,9 @@ class RingBuffer {
// GCTracer collects and prints ONE line after each garbage collector // GCTracer collects and prints ONE line after each garbage collector
// invocation IFF --trace_gc is used. // invocation IFF --trace_gc is used.
// TODO(ernstm): Unit tests. // TODO(ernstm): Unit tests.
class GCTracer BASE_EMBEDDED { class GCTracer {
public: public:
class Scope BASE_EMBEDDED { class Scope {
public: public:
enum ScopeId { enum ScopeId {
EXTERNAL, EXTERNAL,
...@@ -291,6 +293,10 @@ class GCTracer BASE_EMBEDDED { ...@@ -291,6 +293,10 @@ class GCTracer BASE_EMBEDDED {
// Returns 0 if no events have been recorded. // Returns 0 if no events have been recorded.
intptr_t ScavengeSpeedInBytesPerMillisecond() const; intptr_t ScavengeSpeedInBytesPerMillisecond() const;
// Compute the max mark-sweep speed in bytes/millisecond.
// Returns 0 if no events have been recorded.
intptr_t MarkCompactSpeedInBytesPerMillisecond() const;
private: private:
// Print one detailed trace line in name=value format. // Print one detailed trace line in name=value format.
// TODO(ernstm): Move to Heap. // TODO(ernstm): Move to Heap.
......
...@@ -120,12 +120,7 @@ Heap::Heap() ...@@ -120,12 +120,7 @@ Heap::Heap()
store_buffer_(this), store_buffer_(this),
marking_(this), marking_(this),
incremental_marking_(this), incremental_marking_(this),
number_idle_notifications_(0),
last_idle_notification_gc_count_(0),
last_idle_notification_gc_count_init_(false),
mark_sweeps_since_idle_round_started_(0),
gc_count_at_last_idle_gc_(0), gc_count_at_last_idle_gc_(0),
scavenges_since_last_idle_round_(kIdleScavengeThreshold),
full_codegen_bytes_generated_(0), full_codegen_bytes_generated_(0),
crankshaft_codegen_bytes_generated_(0), crankshaft_codegen_bytes_generated_(0),
gcs_since_last_deopt_(0), gcs_since_last_deopt_(0),
...@@ -1559,7 +1554,7 @@ void Heap::Scavenge() { ...@@ -1559,7 +1554,7 @@ void Heap::Scavenge() {
gc_state_ = NOT_IN_GC; gc_state_ = NOT_IN_GC;
scavenges_since_last_idle_round_++; gc_idle_time_handler_.NotifyScavenge();
} }
...@@ -4265,12 +4260,7 @@ void Heap::MakeHeapIterable() { ...@@ -4265,12 +4260,7 @@ void Heap::MakeHeapIterable() {
} }
void Heap::AdvanceIdleIncrementalMarking(int idle_time_in_ms) { void Heap::AdvanceIdleIncrementalMarking(intptr_t step_size) {
intptr_t step_size =
static_cast<size_t>(GCIdleTimeHandler::EstimateMarkingStepSize(
idle_time_in_ms,
tracer_.IncrementalMarkingSpeedInBytesPerMillisecond()));
incremental_marking()->Step(step_size, incremental_marking()->Step(step_size,
IncrementalMarking::NO_GC_VIA_STACK_GUARD, true); IncrementalMarking::NO_GC_VIA_STACK_GUARD, true);
...@@ -4283,7 +4273,7 @@ void Heap::AdvanceIdleIncrementalMarking(int idle_time_in_ms) { ...@@ -4283,7 +4273,7 @@ void Heap::AdvanceIdleIncrementalMarking(int idle_time_in_ms) {
} }
CollectAllGarbage(kReduceMemoryFootprintMask, CollectAllGarbage(kReduceMemoryFootprintMask,
"idle notification: finalize incremental"); "idle notification: finalize incremental");
mark_sweeps_since_idle_round_started_++; gc_idle_time_handler_.NotifyIdleMarkCompact();
gc_count_at_last_idle_gc_ = gc_count_; gc_count_at_last_idle_gc_ = gc_count_;
if (uncommit) { if (uncommit) {
new_space_.Shrink(); new_space_.Shrink();
...@@ -4296,83 +4286,49 @@ void Heap::AdvanceIdleIncrementalMarking(int idle_time_in_ms) { ...@@ -4296,83 +4286,49 @@ void Heap::AdvanceIdleIncrementalMarking(int idle_time_in_ms) {
bool Heap::IdleNotification(int idle_time_in_ms) { bool Heap::IdleNotification(int idle_time_in_ms) {
// If incremental marking is off, we do not perform idle notification. // If incremental marking is off, we do not perform idle notification.
if (!FLAG_incremental_marking) return true; if (!FLAG_incremental_marking) return true;
// Minimal hint that allows to do full GC.
const int kMinHintForFullGC = 100;
isolate()->counters()->gc_idle_time_allotted_in_ms()->AddSample( isolate()->counters()->gc_idle_time_allotted_in_ms()->AddSample(
idle_time_in_ms); idle_time_in_ms);
HistogramTimerScope idle_notification_scope( HistogramTimerScope idle_notification_scope(
isolate_->counters()->gc_idle_notification()); isolate_->counters()->gc_idle_notification());
if (contexts_disposed_ > 0) { GCIdleTimeAction action = gc_idle_time_handler_.Compute(
contexts_disposed_ = 0; idle_time_in_ms, contexts_disposed_, static_cast<size_t>(SizeOfObjects()),
int mark_sweep_time = Min(TimeMarkSweepWouldTakeInMs(), 1000); incremental_marking()->IsStopped(), tracer());
if (idle_time_in_ms >= mark_sweep_time && !FLAG_expose_gc && contexts_disposed_ = 0;
incremental_marking()->IsStopped()) { bool result = false;
switch (action.type) {
case DO_INCREMENTAL_MARKING:
if (incremental_marking()->IsStopped()) {
incremental_marking()->Start();
}
AdvanceIdleIncrementalMarking(action.parameter);
break;
case DO_FULL_GC: {
HistogramTimerScope scope(isolate_->counters()->gc_context()); HistogramTimerScope scope(isolate_->counters()->gc_context());
CollectAllGarbage(kReduceMemoryFootprintMask, const char* message = contexts_disposed_
"idle notification: contexts disposed"); ? "idle notification: contexts disposed"
} else { : "idle notification: finalize idle round";
AdvanceIdleIncrementalMarking(idle_time_in_ms); CollectAllGarbage(kReduceMemoryFootprintMask, message);
} gc_idle_time_handler_.NotifyIdleMarkCompact();
break;
// After context disposal there is likely a lot of garbage remaining, reset
// the idle notification counters in order to trigger more incremental GCs
// on subsequent idle notifications.
StartIdleRound();
return false;
}
// 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,
// Use mark-sweep-compact events to count incremental GCs in a round.
if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) {
if (EnoughGarbageSinceLastIdleRound()) {
StartIdleRound();
} else {
return true;
}
}
int remaining_mark_sweeps =
kMaxMarkSweepsInIdleRound - mark_sweeps_since_idle_round_started_;
if (incremental_marking()->IsStopped()) {
// If there are no more than two GCs left in this idle round and we are
// allowed to do a full GC, then make those GCs full in order to compact
// the code space.
// TODO(ulan): Once we enable code compaction for incremental marking,
// we can get rid of this special case and always start incremental marking.
if (remaining_mark_sweeps <= 2 && idle_time_in_ms >= kMinHintForFullGC) {
CollectAllGarbage(kReduceMemoryFootprintMask,
"idle notification: finalize idle round");
mark_sweeps_since_idle_round_started_++;
} else {
incremental_marking()->Start();
} }
case DO_SCAVENGE:
CollectGarbage(NEW_SPACE, "idle notification: scavenge");
break;
case DO_NOTHING:
result = true;
break;
} }
if (!incremental_marking()->IsStopped()) {
AdvanceIdleIncrementalMarking(idle_time_in_ms);
}
if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) {
FinishIdleRound();
return true;
}
// If the IdleNotifcation is called with a large hint we will wait for // If the IdleNotifcation is called with a large hint we will wait for
// the sweepter threads here. // the sweepter threads here.
// TODO(ulan): move this in GCIdleTimeHandler.
const int kMinHintForFullGC = 100;
if (idle_time_in_ms >= kMinHintForFullGC && if (idle_time_in_ms >= kMinHintForFullGC &&
mark_compact_collector()->sweeping_in_progress()) { mark_compact_collector()->sweeping_in_progress()) {
mark_compact_collector()->EnsureSweepingCompleted(); mark_compact_collector()->EnsureSweepingCompleted();
} }
return false; return result;
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "src/assert-scope.h" #include "src/assert-scope.h"
#include "src/counters.h" #include "src/counters.h"
#include "src/globals.h" #include "src/globals.h"
#include "src/heap/gc-idle-time-handler.h"
#include "src/heap/gc-tracer.h" #include "src/heap/gc-tracer.h"
#include "src/heap/incremental-marking.h" #include "src/heap/incremental-marking.h"
#include "src/heap/mark-compact.h" #include "src/heap/mark-compact.h"
...@@ -1929,30 +1930,7 @@ class Heap { ...@@ -1929,30 +1930,7 @@ class Heap {
void SelectScavengingVisitorsTable(); void SelectScavengingVisitorsTable();
void StartIdleRound() { mark_sweeps_since_idle_round_started_ = 0; } void AdvanceIdleIncrementalMarking(intptr_t step_size);
void FinishIdleRound() {
mark_sweeps_since_idle_round_started_ = kMaxMarkSweepsInIdleRound;
scavenges_since_last_idle_round_ = 0;
}
bool EnoughGarbageSinceLastIdleRound() {
return (scavenges_since_last_idle_round_ >= kIdleScavengeThreshold);
}
// Estimates how many milliseconds a Mark-Sweep would take to complete.
// In idle notification handler we assume that this function will return:
// - a number less than 10 for small heaps, which are less than 8Mb.
// - a number greater than 10 for large heaps, which are greater than 32Mb.
int TimeMarkSweepWouldTakeInMs() {
// Rough estimate of how many megabytes of heap can be processed in 1 ms.
static const int kMbPerMs = 2;
int heap_size_mb = static_cast<int>(SizeOfObjects() / MB);
return heap_size_mb / kMbPerMs;
}
void AdvanceIdleIncrementalMarking(int idle_time_in_ms);
void ClearObjectStats(bool clear_last_time_stats = false); void ClearObjectStats(bool clear_last_time_stats = false);
...@@ -2005,13 +1983,8 @@ class Heap { ...@@ -2005,13 +1983,8 @@ class Heap {
IncrementalMarking incremental_marking_; IncrementalMarking incremental_marking_;
int number_idle_notifications_; GCIdleTimeHandler gc_idle_time_handler_;
unsigned int last_idle_notification_gc_count_;
bool last_idle_notification_gc_count_init_;
int mark_sweeps_since_idle_round_started_;
unsigned int gc_count_at_last_idle_gc_; unsigned int gc_count_at_last_idle_gc_;
int scavenges_since_last_idle_round_;
// These two counters are monotomically increasing and never reset. // These two counters are monotomically increasing and never reset.
size_t full_codegen_bytes_generated_; size_t full_codegen_bytes_generated_;
...@@ -2029,7 +2002,7 @@ class Heap { ...@@ -2029,7 +2002,7 @@ class Heap {
static const int kAllocationSiteScratchpadSize = 256; static const int kAllocationSiteScratchpadSize = 256;
int allocation_sites_scratchpad_length_; int allocation_sites_scratchpad_length_;
static const int kMaxMarkSweepsInIdleRound = 7; static const int kMaxMarkCompactsInIdleRound = 7;
static const int kIdleScavengeThreshold = 5; 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.
......
...@@ -45,5 +45,29 @@ TEST(EstimateMarkingStepSizeTest, EstimateMarkingStepSizeOverflow2) { ...@@ -45,5 +45,29 @@ TEST(EstimateMarkingStepSizeTest, EstimateMarkingStepSizeOverflow2) {
step_size); step_size);
} }
TEST(EstimateMarkCompactTimeTest, EstimateMarkCompactTimeInitial) {
size_t size = 100 * MB;
size_t time = GCIdleTimeHandler::EstimateMarkCompactTime(size, 0);
EXPECT_EQ(size / GCIdleTimeHandler::kInitialConservativeMarkCompactSpeed,
time);
}
TEST(EstimateMarkCompactTimeTest, EstimateMarkCompactTimeNonZero) {
size_t size = 100 * MB;
size_t speed = 10 * KB;
size_t time = GCIdleTimeHandler::EstimateMarkCompactTime(size, speed);
EXPECT_EQ(size / speed, time);
}
TEST(EstimateMarkCompactTimeTest, EstimateMarkCompactTimeMax) {
size_t size = std::numeric_limits<size_t>::max();
size_t speed = 1;
size_t time = GCIdleTimeHandler::EstimateMarkCompactTime(size, speed);
EXPECT_EQ(GCIdleTimeHandler::kMaxMarkCompactTimeInMs, time);
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
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