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
This commit is contained in:
ulan@chromium.org 2014-08-21 14:42:22 +00:00
parent 758dd167f7
commit 53fdf75be1
7 changed files with 224 additions and 115 deletions

View File

@ -3,12 +3,14 @@
// found in the LICENSE file.
#include "src/heap/gc-idle-time-handler.h"
#include "src/heap/gc-tracer.h"
#include "src/utils.h"
namespace v8 {
namespace internal {
const double GCIdleTimeHandler::kConservativeTimeRatio = 0.9;
const size_t GCIdleTimeHandler::kMaxMarkCompactTimeInMs = 1000000;
size_t GCIdleTimeHandler::EstimateMarkingStepSize(
@ -16,14 +18,13 @@ size_t GCIdleTimeHandler::EstimateMarkingStepSize(
DCHECK(idle_time_in_ms > 0);
if (marking_speed_in_bytes_per_ms == 0) {
marking_speed_in_bytes_per_ms =
GCIdleTimeHandler::kInitialConservativeMarkingSpeed;
marking_speed_in_bytes_per_ms = kInitialConservativeMarkingSpeed;
}
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) {
// In the case of an overflow we return maximum marking step size.
return GCIdleTimeHandler::kMaximumMarkingStepSize;
return kMaximumMarkingStepSize;
}
if (marking_step_size > kMaximumMarkingStepSize)
@ -32,5 +33,51 @@ size_t GCIdleTimeHandler::EstimateMarkingStepSize(
return static_cast<size_t>(marking_step_size *
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);
}
}
}

View File

@ -10,13 +10,51 @@
namespace v8 {
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
// operations are executing during IdleNotification.
class GCIdleTimeHandler {
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
// mark with a conservative lower bound for the marking speed.
static const size_t kInitialConservativeMarkingSpeed = 100 * KB;
@ -28,7 +66,55 @@ class GCIdleTimeHandler {
// idle_time_in_ms. Hence, we conservatively prune our workload estimate.
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:
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);
};

View File

@ -418,5 +418,22 @@ intptr_t GCTracer::ScavengeSpeedInBytesPerMillisecond() const {
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

View File

@ -5,6 +5,8 @@
#ifndef V8_HEAP_GC_TRACER_H_
#define V8_HEAP_GC_TRACER_H_
#include "src/base/platform/platform.h"
namespace v8 {
namespace internal {
@ -81,9 +83,9 @@ class RingBuffer {
// GCTracer collects and prints ONE line after each garbage collector
// invocation IFF --trace_gc is used.
// TODO(ernstm): Unit tests.
class GCTracer BASE_EMBEDDED {
class GCTracer {
public:
class Scope BASE_EMBEDDED {
class Scope {
public:
enum ScopeId {
EXTERNAL,
@ -291,6 +293,10 @@ class GCTracer BASE_EMBEDDED {
// Returns 0 if no events have been recorded.
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:
// Print one detailed trace line in name=value format.
// TODO(ernstm): Move to Heap.

View File

@ -120,12 +120,7 @@ Heap::Heap()
store_buffer_(this),
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),
scavenges_since_last_idle_round_(kIdleScavengeThreshold),
full_codegen_bytes_generated_(0),
crankshaft_codegen_bytes_generated_(0),
gcs_since_last_deopt_(0),
@ -1559,7 +1554,7 @@ void Heap::Scavenge() {
gc_state_ = NOT_IN_GC;
scavenges_since_last_idle_round_++;
gc_idle_time_handler_.NotifyScavenge();
}
@ -4265,12 +4260,7 @@ void Heap::MakeHeapIterable() {
}
void Heap::AdvanceIdleIncrementalMarking(int idle_time_in_ms) {
intptr_t step_size =
static_cast<size_t>(GCIdleTimeHandler::EstimateMarkingStepSize(
idle_time_in_ms,
tracer_.IncrementalMarkingSpeedInBytesPerMillisecond()));
void Heap::AdvanceIdleIncrementalMarking(intptr_t step_size) {
incremental_marking()->Step(step_size,
IncrementalMarking::NO_GC_VIA_STACK_GUARD, true);
@ -4283,7 +4273,7 @@ void Heap::AdvanceIdleIncrementalMarking(int idle_time_in_ms) {
}
CollectAllGarbage(kReduceMemoryFootprintMask,
"idle notification: finalize incremental");
mark_sweeps_since_idle_round_started_++;
gc_idle_time_handler_.NotifyIdleMarkCompact();
gc_count_at_last_idle_gc_ = gc_count_;
if (uncommit) {
new_space_.Shrink();
@ -4296,83 +4286,49 @@ void Heap::AdvanceIdleIncrementalMarking(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 (!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(
idle_time_in_ms);
HistogramTimerScope idle_notification_scope(
isolate_->counters()->gc_idle_notification());
if (contexts_disposed_ > 0) {
contexts_disposed_ = 0;
int mark_sweep_time = Min(TimeMarkSweepWouldTakeInMs(), 1000);
if (idle_time_in_ms >= mark_sweep_time && !FLAG_expose_gc &&
incremental_marking()->IsStopped()) {
GCIdleTimeAction action = gc_idle_time_handler_.Compute(
idle_time_in_ms, contexts_disposed_, static_cast<size_t>(SizeOfObjects()),
incremental_marking()->IsStopped(), tracer());
contexts_disposed_ = 0;
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());
CollectAllGarbage(kReduceMemoryFootprintMask,
"idle notification: contexts disposed");
} else {
AdvanceIdleIncrementalMarking(idle_time_in_ms);
const char* message = contexts_disposed_
? "idle notification: contexts disposed"
: "idle notification: finalize idle round";
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;
case DO_SCAVENGE:
CollectGarbage(NEW_SPACE, "idle notification: scavenge");
break;
case DO_NOTHING:
result = true;
break;
}
// 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();
}
}
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
// the sweepter threads here.
// TODO(ulan): move this in GCIdleTimeHandler.
const int kMinHintForFullGC = 100;
if (idle_time_in_ms >= kMinHintForFullGC &&
mark_compact_collector()->sweeping_in_progress()) {
mark_compact_collector()->EnsureSweepingCompleted();
}
return false;
return result;
}

View File

@ -11,6 +11,7 @@
#include "src/assert-scope.h"
#include "src/counters.h"
#include "src/globals.h"
#include "src/heap/gc-idle-time-handler.h"
#include "src/heap/gc-tracer.h"
#include "src/heap/incremental-marking.h"
#include "src/heap/mark-compact.h"
@ -1929,30 +1930,7 @@ class Heap {
void SelectScavengingVisitorsTable();
void StartIdleRound() { mark_sweeps_since_idle_round_started_ = 0; }
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 AdvanceIdleIncrementalMarking(intptr_t step_size);
void ClearObjectStats(bool clear_last_time_stats = false);
@ -2005,13 +1983,8 @@ class Heap {
IncrementalMarking incremental_marking_;
int number_idle_notifications_;
unsigned int last_idle_notification_gc_count_;
bool last_idle_notification_gc_count_init_;
int mark_sweeps_since_idle_round_started_;
GCIdleTimeHandler gc_idle_time_handler_;
unsigned int gc_count_at_last_idle_gc_;
int scavenges_since_last_idle_round_;
// These two counters are monotomically increasing and never reset.
size_t full_codegen_bytes_generated_;
@ -2029,7 +2002,7 @@ class Heap {
static const int kAllocationSiteScratchpadSize = 256;
int allocation_sites_scratchpad_length_;
static const int kMaxMarkSweepsInIdleRound = 7;
static const int kMaxMarkCompactsInIdleRound = 7;
static const int kIdleScavengeThreshold = 5;
// Shared state read by the scavenge collector and set by ScavengeObject.

View File

@ -45,5 +45,29 @@ TEST(EstimateMarkingStepSizeTest, EstimateMarkingStepSizeOverflow2) {
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 v8