Use idle task to perform incremental marking steps.
This moves incremental marking steps from gc-idle-time-handler and heap to the new incremental marking task. BUG=chromium:490559 LOG=NO Review URL: https://codereview.chromium.org/1265423002 Cr-Commit-Position: refs/heads/master@{#30641}
This commit is contained in:
parent
244cc0a6a9
commit
057514d3fa
2
BUILD.gn
2
BUILD.gn
@ -968,6 +968,8 @@ source_set("v8_base") {
|
||||
"src/heap/heap-inl.h",
|
||||
"src/heap/heap.cc",
|
||||
"src/heap/heap.h",
|
||||
"src/heap/incremental-marking-job.cc",
|
||||
"src/heap/incremental-marking-job.h",
|
||||
"src/heap/incremental-marking.cc",
|
||||
"src/heap/incremental-marking.h",
|
||||
"src/heap/mark-compact-inl.h",
|
||||
|
@ -26,9 +26,8 @@ void GCIdleTimeAction::Print() {
|
||||
case DO_NOTHING:
|
||||
PrintF("no action");
|
||||
break;
|
||||
case DO_INCREMENTAL_MARKING:
|
||||
PrintF("incremental marking with step %" V8_PTR_PREFIX "d / ms",
|
||||
parameter);
|
||||
case DO_INCREMENTAL_STEP:
|
||||
PrintF("incremental step");
|
||||
if (additional_work) {
|
||||
PrintF("; finalized marking");
|
||||
}
|
||||
@ -39,9 +38,6 @@ void GCIdleTimeAction::Print() {
|
||||
case DO_FULL_GC:
|
||||
PrintF("full GC");
|
||||
break;
|
||||
case DO_FINALIZE_SWEEPING:
|
||||
PrintF("finalize sweeping");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,22 +267,11 @@ GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms,
|
||||
return GCIdleTimeAction::Scavenge();
|
||||
}
|
||||
|
||||
if (heap_state.sweeping_in_progress) {
|
||||
if (heap_state.sweeping_completed) {
|
||||
return GCIdleTimeAction::FinalizeSweeping();
|
||||
} else {
|
||||
return NothingOrDone(idle_time_in_ms);
|
||||
}
|
||||
}
|
||||
|
||||
if (!FLAG_incremental_marking || heap_state.incremental_marking_stopped) {
|
||||
return GCIdleTimeAction::Done();
|
||||
}
|
||||
|
||||
size_t step_size = EstimateMarkingStepSize(
|
||||
static_cast<size_t>(kIncrementalMarkingStepTimeInMs),
|
||||
heap_state.incremental_marking_speed_in_bytes_per_ms);
|
||||
return GCIdleTimeAction::IncrementalMarking(step_size);
|
||||
return GCIdleTimeAction::IncrementalStep();
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,10 +13,9 @@ namespace internal {
|
||||
enum GCIdleTimeActionType {
|
||||
DONE,
|
||||
DO_NOTHING,
|
||||
DO_INCREMENTAL_MARKING,
|
||||
DO_INCREMENTAL_STEP,
|
||||
DO_SCAVENGE,
|
||||
DO_FULL_GC,
|
||||
DO_FINALIZE_SWEEPING
|
||||
};
|
||||
|
||||
|
||||
@ -25,7 +24,6 @@ class GCIdleTimeAction {
|
||||
static GCIdleTimeAction Done() {
|
||||
GCIdleTimeAction result;
|
||||
result.type = DONE;
|
||||
result.parameter = 0;
|
||||
result.additional_work = false;
|
||||
return result;
|
||||
}
|
||||
@ -33,15 +31,13 @@ class GCIdleTimeAction {
|
||||
static GCIdleTimeAction Nothing() {
|
||||
GCIdleTimeAction result;
|
||||
result.type = DO_NOTHING;
|
||||
result.parameter = 0;
|
||||
result.additional_work = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
static GCIdleTimeAction IncrementalMarking(intptr_t step_size) {
|
||||
static GCIdleTimeAction IncrementalStep() {
|
||||
GCIdleTimeAction result;
|
||||
result.type = DO_INCREMENTAL_MARKING;
|
||||
result.parameter = step_size;
|
||||
result.type = DO_INCREMENTAL_STEP;
|
||||
result.additional_work = false;
|
||||
return result;
|
||||
}
|
||||
@ -49,7 +45,6 @@ class GCIdleTimeAction {
|
||||
static GCIdleTimeAction Scavenge() {
|
||||
GCIdleTimeAction result;
|
||||
result.type = DO_SCAVENGE;
|
||||
result.parameter = 0;
|
||||
result.additional_work = false;
|
||||
return result;
|
||||
}
|
||||
@ -57,15 +52,6 @@ class GCIdleTimeAction {
|
||||
static GCIdleTimeAction FullGC() {
|
||||
GCIdleTimeAction result;
|
||||
result.type = DO_FULL_GC;
|
||||
result.parameter = 0;
|
||||
result.additional_work = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
static GCIdleTimeAction FinalizeSweeping() {
|
||||
GCIdleTimeAction result;
|
||||
result.type = DO_FINALIZE_SWEEPING;
|
||||
result.parameter = 0;
|
||||
result.additional_work = false;
|
||||
return result;
|
||||
}
|
||||
@ -73,7 +59,6 @@ class GCIdleTimeAction {
|
||||
void Print();
|
||||
|
||||
GCIdleTimeActionType type;
|
||||
intptr_t parameter;
|
||||
bool additional_work;
|
||||
};
|
||||
|
||||
|
@ -4535,10 +4535,12 @@ void Heap::FinalizeIncrementalMarkingIfComplete(const char* comment) {
|
||||
}
|
||||
|
||||
|
||||
bool Heap::TryFinalizeIdleIncrementalMarking(
|
||||
double idle_time_in_ms, size_t size_of_objects,
|
||||
size_t final_incremental_mark_compact_speed_in_bytes_per_ms) {
|
||||
if (FLAG_overapproximate_weak_closure && incremental_marking()->IsMarking() &&
|
||||
bool Heap::TryFinalizeIdleIncrementalMarking(double idle_time_in_ms) {
|
||||
size_t size_of_objects = static_cast<size_t>(SizeOfObjects());
|
||||
size_t final_incremental_mark_compact_speed_in_bytes_per_ms =
|
||||
static_cast<size_t>(
|
||||
tracer()->FinalIncrementalMarkCompactSpeedInBytesPerMillisecond());
|
||||
if (FLAG_overapproximate_weak_closure &&
|
||||
(incremental_marking()->IsReadyToOverApproximateWeakClosure() ||
|
||||
(!incremental_marking()->weak_closure_was_overapproximated() &&
|
||||
mark_compact_collector_.marking_deque()->IsEmpty() &&
|
||||
@ -4565,19 +4567,9 @@ GCIdleTimeHandler::HeapState Heap::ComputeHeapState() {
|
||||
heap_state.contexts_disposed = contexts_disposed_;
|
||||
heap_state.contexts_disposal_rate =
|
||||
tracer()->ContextDisposalRateInMilliseconds();
|
||||
heap_state.size_of_objects = static_cast<size_t>(SizeOfObjects());
|
||||
heap_state.incremental_marking_stopped = incremental_marking()->IsStopped();
|
||||
heap_state.sweeping_in_progress =
|
||||
mark_compact_collector()->sweeping_in_progress();
|
||||
heap_state.sweeping_completed =
|
||||
mark_compact_collector()->IsSweepingCompleted();
|
||||
heap_state.mark_compact_speed_in_bytes_per_ms =
|
||||
static_cast<size_t>(tracer()->MarkCompactSpeedInBytesPerMillisecond());
|
||||
heap_state.incremental_marking_speed_in_bytes_per_ms = static_cast<size_t>(
|
||||
tracer()->IncrementalMarkingSpeedInBytesPerMillisecond());
|
||||
heap_state.final_incremental_mark_compact_speed_in_bytes_per_ms =
|
||||
static_cast<size_t>(
|
||||
tracer()->FinalIncrementalMarkCompactSpeedInBytesPerMillisecond());
|
||||
heap_state.scavenge_speed_in_bytes_per_ms =
|
||||
static_cast<size_t>(tracer()->ScavengeSpeedInBytesPerMillisecond());
|
||||
heap_state.used_new_space_size = new_space_.Size();
|
||||
@ -4622,14 +4614,15 @@ bool Heap::PerformIdleTimeAction(GCIdleTimeAction action,
|
||||
case DONE:
|
||||
result = true;
|
||||
break;
|
||||
case DO_INCREMENTAL_MARKING: {
|
||||
const double remaining_idle_time_in_ms =
|
||||
AdvanceIncrementalMarking(action.parameter, deadline_in_ms,
|
||||
IncrementalMarking::IdleStepActions());
|
||||
if (remaining_idle_time_in_ms > 0.0) {
|
||||
action.additional_work = TryFinalizeIdleIncrementalMarking(
|
||||
remaining_idle_time_in_ms, heap_state.size_of_objects,
|
||||
heap_state.final_incremental_mark_compact_speed_in_bytes_per_ms);
|
||||
case DO_INCREMENTAL_STEP: {
|
||||
if (incremental_marking()->incremental_marking_job()->IdleTaskPending()) {
|
||||
result = true;
|
||||
} else {
|
||||
incremental_marking()
|
||||
->incremental_marking_job()
|
||||
->NotifyIdleTaskProgress();
|
||||
result = IncrementalMarkingJob::IdleTask::Step(this, deadline_in_ms) ==
|
||||
IncrementalMarkingJob::IdleTask::kDone;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -4642,9 +4635,6 @@ bool Heap::PerformIdleTimeAction(GCIdleTimeAction action,
|
||||
case DO_SCAVENGE:
|
||||
CollectGarbage(NEW_SPACE, "idle notification: scavenge");
|
||||
break;
|
||||
case DO_FINALIZE_SWEEPING:
|
||||
mark_compact_collector()->EnsureSweepingCompleted();
|
||||
break;
|
||||
case DO_NOTHING:
|
||||
break;
|
||||
}
|
||||
|
@ -1278,6 +1278,8 @@ class Heap {
|
||||
|
||||
void FinalizeIncrementalMarkingIfComplete(const char* comment);
|
||||
|
||||
bool TryFinalizeIdleIncrementalMarking(double idle_time_in_ms);
|
||||
|
||||
IncrementalMarking* incremental_marking() { return &incremental_marking_; }
|
||||
|
||||
// ===========================================================================
|
||||
|
144
src/heap/incremental-marking-job.cc
Normal file
144
src/heap/incremental-marking-job.cc
Normal file
@ -0,0 +1,144 @@
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/heap/incremental-marking-job.h"
|
||||
|
||||
#include "src/base/platform/time.h"
|
||||
#include "src/heap/heap-inl.h"
|
||||
#include "src/heap/heap.h"
|
||||
#include "src/heap/incremental-marking.h"
|
||||
#include "src/isolate.h"
|
||||
#include "src/v8.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
|
||||
void IncrementalMarkingJob::Start(Heap* heap) {
|
||||
DCHECK(!heap->incremental_marking()->IsStopped());
|
||||
// We don't need to reset the flags because tasks from the previous job
|
||||
// can still be pending. We just want to ensure that tasks are posted
|
||||
// if they are not pending.
|
||||
// If delayed task is pending and made_progress_since_last_delayed_task_ is
|
||||
// true, then the delayed task will clear that flag when it is rescheduled.
|
||||
ScheduleIdleTask(heap);
|
||||
ScheduleDelayedTask(heap);
|
||||
}
|
||||
|
||||
|
||||
void IncrementalMarkingJob::NotifyIdleTask() { idle_task_pending_ = false; }
|
||||
|
||||
|
||||
void IncrementalMarkingJob::NotifyDelayedTask() {
|
||||
delayed_task_pending_ = false;
|
||||
}
|
||||
|
||||
|
||||
void IncrementalMarkingJob::NotifyIdleTaskProgress() {
|
||||
made_progress_since_last_delayed_task_ = true;
|
||||
}
|
||||
|
||||
|
||||
void IncrementalMarkingJob::ScheduleIdleTask(Heap* heap) {
|
||||
if (!idle_task_pending_) {
|
||||
v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(heap->isolate());
|
||||
if (V8::GetCurrentPlatform()->IdleTasksEnabled(isolate)) {
|
||||
idle_task_pending_ = true;
|
||||
auto task = new IdleTask(heap->isolate(), this);
|
||||
V8::GetCurrentPlatform()->CallIdleOnForegroundThread(isolate, task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IncrementalMarkingJob::ScheduleDelayedTask(Heap* heap) {
|
||||
if (!delayed_task_pending_) {
|
||||
v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(heap->isolate());
|
||||
delayed_task_pending_ = true;
|
||||
made_progress_since_last_delayed_task_ = false;
|
||||
auto task = new DelayedTask(heap->isolate(), this);
|
||||
V8::GetCurrentPlatform()->CallDelayedOnForegroundThread(isolate, task,
|
||||
kDelayInSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
IncrementalMarkingJob::IdleTask::Progress IncrementalMarkingJob::IdleTask::Step(
|
||||
Heap* heap, double deadline_in_ms) {
|
||||
IncrementalMarking* incremental_marking = heap->incremental_marking();
|
||||
MarkCompactCollector* mark_compact_collector = heap->mark_compact_collector();
|
||||
if (incremental_marking->IsStopped()) {
|
||||
return kDone;
|
||||
}
|
||||
if (mark_compact_collector->sweeping_in_progress()) {
|
||||
if (mark_compact_collector->IsSweepingCompleted()) {
|
||||
mark_compact_collector->EnsureSweepingCompleted();
|
||||
}
|
||||
return kMoreWork;
|
||||
}
|
||||
const double remaining_idle_time_in_ms = heap->AdvanceIncrementalMarking(
|
||||
0, deadline_in_ms, IncrementalMarking::IdleStepActions());
|
||||
if (remaining_idle_time_in_ms > 0.0) {
|
||||
heap->TryFinalizeIdleIncrementalMarking(remaining_idle_time_in_ms);
|
||||
}
|
||||
return incremental_marking->IsStopped() ? kDone : kMoreWork;
|
||||
}
|
||||
|
||||
|
||||
void IncrementalMarkingJob::IdleTask::RunInternal(double deadline_in_seconds) {
|
||||
double deadline_in_ms =
|
||||
deadline_in_seconds *
|
||||
static_cast<double>(base::Time::kMillisecondsPerSecond);
|
||||
Heap* heap = isolate_->heap();
|
||||
double start_ms = heap->MonotonicallyIncreasingTimeInMs();
|
||||
job_->NotifyIdleTask();
|
||||
job_->NotifyIdleTaskProgress();
|
||||
if (Step(heap, deadline_in_ms) == kMoreWork) {
|
||||
job_->ScheduleIdleTask(heap);
|
||||
}
|
||||
if (FLAG_trace_idle_notification) {
|
||||
double current_time_ms = heap->MonotonicallyIncreasingTimeInMs();
|
||||
double idle_time_in_ms = deadline_in_ms - start_ms;
|
||||
double deadline_difference = deadline_in_ms - current_time_ms;
|
||||
PrintIsolate(isolate_, "%8.0f ms: ", isolate_->time_millis_since_init());
|
||||
PrintF(
|
||||
"Idle task: requested idle time %.2f ms, used idle time %.2f "
|
||||
"ms, deadline usage %.2f ms\n",
|
||||
idle_time_in_ms, idle_time_in_ms - deadline_difference,
|
||||
deadline_difference);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IncrementalMarkingJob::DelayedTask::Step(Heap* heap) {
|
||||
const int kIncrementalMarkingDelayMs = 50;
|
||||
double deadline =
|
||||
heap->MonotonicallyIncreasingTimeInMs() + kIncrementalMarkingDelayMs;
|
||||
heap->AdvanceIncrementalMarking(
|
||||
0, deadline, i::IncrementalMarking::StepActions(
|
||||
i::IncrementalMarking::NO_GC_VIA_STACK_GUARD,
|
||||
i::IncrementalMarking::FORCE_MARKING,
|
||||
i::IncrementalMarking::FORCE_COMPLETION));
|
||||
heap->FinalizeIncrementalMarkingIfComplete(
|
||||
"Incremental marking task: finalize incremental marking");
|
||||
}
|
||||
|
||||
|
||||
void IncrementalMarkingJob::DelayedTask::RunInternal() {
|
||||
Heap* heap = isolate_->heap();
|
||||
job_->NotifyDelayedTask();
|
||||
IncrementalMarking* incremental_marking = heap->incremental_marking();
|
||||
if (!incremental_marking->IsStopped()) {
|
||||
if (job_->ShouldForceMarkingStep()) {
|
||||
Step(heap);
|
||||
}
|
||||
// The Step() above could have finished incremental marking.
|
||||
if (!incremental_marking->IsStopped()) {
|
||||
job_->ScheduleDelayedTask(heap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
81
src/heap/incremental-marking-job.h
Normal file
81
src/heap/incremental-marking-job.h
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_HEAP_INCREMENTAL_MARKING_JOB_H_
|
||||
#define V8_HEAP_INCREMENTAL_MARKING_JOB_H_
|
||||
|
||||
#include "src/cancelable-task.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class Heap;
|
||||
class Isolate;
|
||||
|
||||
// The incremental marking job uses platform tasks to perform incremental
|
||||
// marking steps. The job posts an idle and a delayed task with a large delay.
|
||||
// The delayed task performs steps only if the idle task is not making progress.
|
||||
// We expect this to be a rare event since incremental marking should finish
|
||||
// quickly with the help of the mutator and the idle task.
|
||||
// The delayed task guarantees that we eventually finish incremental marking
|
||||
// even if the mutator becomes idle and the platform stops running idle tasks,
|
||||
// which can happen for background tabs in Chrome.
|
||||
class IncrementalMarkingJob {
|
||||
public:
|
||||
class IdleTask : public CancelableIdleTask {
|
||||
public:
|
||||
explicit IdleTask(Isolate* isolate, IncrementalMarkingJob* job)
|
||||
: CancelableIdleTask(isolate), job_(job) {}
|
||||
enum Progress { kDone, kMoreWork };
|
||||
static Progress Step(Heap* heap, double deadline_in_ms);
|
||||
// CancelableIdleTask overrides.
|
||||
void RunInternal(double deadline_in_seconds) override;
|
||||
|
||||
private:
|
||||
IncrementalMarkingJob* job_;
|
||||
};
|
||||
|
||||
class DelayedTask : public CancelableTask {
|
||||
public:
|
||||
explicit DelayedTask(Isolate* isolate, IncrementalMarkingJob* job)
|
||||
: CancelableTask(isolate), job_(job) {}
|
||||
static void Step(Heap* heap);
|
||||
// CancelableTask overrides.
|
||||
void RunInternal() override;
|
||||
|
||||
private:
|
||||
IncrementalMarkingJob* job_;
|
||||
};
|
||||
|
||||
// Delay of the delayed task.
|
||||
static const int kDelayInSeconds = 5;
|
||||
|
||||
IncrementalMarkingJob()
|
||||
: idle_task_pending_(false),
|
||||
delayed_task_pending_(false),
|
||||
made_progress_since_last_delayed_task_(false) {}
|
||||
|
||||
bool ShouldForceMarkingStep() {
|
||||
return !made_progress_since_last_delayed_task_;
|
||||
}
|
||||
|
||||
bool IdleTaskPending() { return idle_task_pending_; }
|
||||
|
||||
void Start(Heap* heap);
|
||||
|
||||
void NotifyIdleTask();
|
||||
void NotifyDelayedTask();
|
||||
void NotifyIdleTaskProgress();
|
||||
void ScheduleIdleTask(Heap* heap);
|
||||
void ScheduleDelayedTask(Heap* heap);
|
||||
|
||||
private:
|
||||
bool idle_task_pending_;
|
||||
bool delayed_task_pending_;
|
||||
bool made_progress_since_last_delayed_task_;
|
||||
};
|
||||
}
|
||||
} // namespace v8::internal
|
||||
|
||||
#endif // V8_HEAP_INCREMENTAL_MARKING_JOB_H_
|
@ -11,6 +11,7 @@
|
||||
#include "src/heap/mark-compact-inl.h"
|
||||
#include "src/heap/objects-visiting.h"
|
||||
#include "src/heap/objects-visiting-inl.h"
|
||||
#include "src/v8.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -486,6 +487,7 @@ void IncrementalMarking::Start(const char* reason) {
|
||||
}
|
||||
|
||||
heap_->new_space()->LowerInlineAllocationLimit(kAllocatedThreshold);
|
||||
incremental_marking_job()->Start(heap_);
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,14 +5,15 @@
|
||||
#ifndef V8_HEAP_INCREMENTAL_MARKING_H_
|
||||
#define V8_HEAP_INCREMENTAL_MARKING_H_
|
||||
|
||||
#include "src/cancelable-task.h"
|
||||
#include "src/execution.h"
|
||||
#include "src/heap/incremental-marking-job.h"
|
||||
#include "src/heap/mark-compact.h"
|
||||
#include "src/objects.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
|
||||
class IncrementalMarking {
|
||||
public:
|
||||
enum State { STOPPED, SWEEPING, MARKING, COMPLETE };
|
||||
@ -197,6 +198,10 @@ class IncrementalMarking {
|
||||
|
||||
Heap* heap() const { return heap_; }
|
||||
|
||||
IncrementalMarkingJob* incremental_marking_job() {
|
||||
return &incremental_marking_job_;
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t SpaceLeftInOldSpace();
|
||||
|
||||
@ -255,6 +260,8 @@ class IncrementalMarking {
|
||||
|
||||
GCRequestType request_type_;
|
||||
|
||||
IncrementalMarkingJob incremental_marking_job_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(IncrementalMarking);
|
||||
};
|
||||
}
|
||||
|
@ -119,6 +119,9 @@ v8::Platform* V8::GetCurrentPlatform() {
|
||||
}
|
||||
|
||||
|
||||
void V8::SetPlatformForTesting(v8::Platform* platform) { platform_ = platform; }
|
||||
|
||||
|
||||
void V8::SetNativesBlob(StartupData* natives_blob) {
|
||||
#ifdef V8_USE_EXTERNAL_STARTUP_DATA
|
||||
base::CallOnce(&init_natives_once, &SetNativesFromFile, natives_blob);
|
||||
|
3
src/v8.h
3
src/v8.h
@ -26,6 +26,9 @@ class V8 : public AllStatic {
|
||||
static void InitializePlatform(v8::Platform* platform);
|
||||
static void ShutdownPlatform();
|
||||
static v8::Platform* GetCurrentPlatform();
|
||||
// Replaces the current platform with the given platform.
|
||||
// Should be used only for testing.
|
||||
static void SetPlatformForTesting(v8::Platform* platform);
|
||||
|
||||
static void SetNativesBlob(StartupData* natives_blob);
|
||||
static void SetSnapshotBlob(StartupData* snapshot_blob);
|
||||
|
@ -133,6 +133,7 @@
|
||||
'test-heap-profiler.cc',
|
||||
'test-hydrogen-types.cc',
|
||||
'test-identity-map.cc',
|
||||
'test-incremental-marking.cc',
|
||||
'test-list.cc',
|
||||
'test-liveedit.cc',
|
||||
'test-lockers.cc',
|
||||
|
168
test/cctest/test-incremental-marking.cc
Normal file
168
test/cctest/test-incremental-marking.cc
Normal file
@ -0,0 +1,168 @@
|
||||
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "src/v8.h"
|
||||
|
||||
#include "src/full-codegen/full-codegen.h"
|
||||
#include "src/global-handles.h"
|
||||
#include "test/cctest/cctest.h"
|
||||
|
||||
using v8::IdleTask;
|
||||
using v8::Task;
|
||||
using v8::Isolate;
|
||||
|
||||
|
||||
class MockPlatform : public v8::Platform {
|
||||
public:
|
||||
explicit MockPlatform(v8::Platform* platform)
|
||||
: platform_(platform), idle_task_(nullptr), delayed_task_(nullptr) {}
|
||||
virtual ~MockPlatform() {
|
||||
delete idle_task_;
|
||||
delete delayed_task_;
|
||||
}
|
||||
|
||||
void CallOnBackgroundThread(Task* task,
|
||||
ExpectedRuntime expected_runtime) override {
|
||||
platform_->CallOnBackgroundThread(task, expected_runtime);
|
||||
}
|
||||
|
||||
void CallOnForegroundThread(Isolate* isolate, Task* task) override {
|
||||
platform_->CallOnForegroundThread(isolate, task);
|
||||
}
|
||||
|
||||
void CallDelayedOnForegroundThread(Isolate* isolate, Task* task,
|
||||
double delay_in_seconds) override {
|
||||
if (delayed_task_ != nullptr) {
|
||||
delete delayed_task_;
|
||||
}
|
||||
delayed_task_ = task;
|
||||
}
|
||||
|
||||
double MonotonicallyIncreasingTime() override {
|
||||
return platform_->MonotonicallyIncreasingTime();
|
||||
}
|
||||
|
||||
void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override {
|
||||
CHECK(nullptr == idle_task_);
|
||||
idle_task_ = task;
|
||||
}
|
||||
|
||||
bool IdleTasksEnabled(Isolate* isolate) override { return true; }
|
||||
|
||||
bool PendingIdleTask() { return idle_task_ != nullptr; }
|
||||
|
||||
void PerformIdleTask(double idle_time_in_seconds) {
|
||||
IdleTask* task = idle_task_;
|
||||
idle_task_ = nullptr;
|
||||
task->Run(MonotonicallyIncreasingTime() + idle_time_in_seconds);
|
||||
delete task;
|
||||
}
|
||||
|
||||
bool PendingDelayedTask() { return delayed_task_ != nullptr; }
|
||||
|
||||
void PerformDelayedTask() {
|
||||
Task* task = delayed_task_;
|
||||
delayed_task_ = nullptr;
|
||||
task->Run();
|
||||
delete task;
|
||||
}
|
||||
|
||||
private:
|
||||
v8::Platform* platform_;
|
||||
IdleTask* idle_task_;
|
||||
Task* delayed_task_;
|
||||
};
|
||||
|
||||
|
||||
TEST(IncrementalMarkingUsingIdleTasks) {
|
||||
if (!i::FLAG_incremental_marking) return;
|
||||
CcTest::InitializeVM();
|
||||
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
|
||||
MockPlatform platform(old_platform);
|
||||
i::V8::SetPlatformForTesting(&platform);
|
||||
SimulateFullSpace(CcTest::heap()->old_space());
|
||||
i::IncrementalMarking* marking = CcTest::heap()->incremental_marking();
|
||||
marking->Stop();
|
||||
marking->Start();
|
||||
CHECK(platform.PendingIdleTask());
|
||||
const double kLongIdleTimeInSeconds = 1;
|
||||
const double kShortIdleTimeInSeconds = 0.010;
|
||||
const int kShortStepCount = 10;
|
||||
for (int i = 0; i < kShortStepCount && platform.PendingIdleTask(); i++) {
|
||||
platform.PerformIdleTask(kShortIdleTimeInSeconds);
|
||||
}
|
||||
while (platform.PendingIdleTask()) {
|
||||
platform.PerformIdleTask(kLongIdleTimeInSeconds);
|
||||
}
|
||||
CHECK(marking->IsStopped());
|
||||
i::V8::SetPlatformForTesting(old_platform);
|
||||
}
|
||||
|
||||
|
||||
TEST(IncrementalMarkingUsingIdleTasksAfterGC) {
|
||||
if (!i::FLAG_incremental_marking) return;
|
||||
CcTest::InitializeVM();
|
||||
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
|
||||
MockPlatform platform(old_platform);
|
||||
i::V8::SetPlatformForTesting(&platform);
|
||||
SimulateFullSpace(CcTest::heap()->old_space());
|
||||
CcTest::heap()->CollectAllGarbage();
|
||||
i::IncrementalMarking* marking = CcTest::heap()->incremental_marking();
|
||||
marking->Stop();
|
||||
marking->Start();
|
||||
CHECK(platform.PendingIdleTask());
|
||||
const double kLongIdleTimeInSeconds = 1;
|
||||
const double kShortIdleTimeInSeconds = 0.010;
|
||||
const int kShortStepCount = 10;
|
||||
for (int i = 0; i < kShortStepCount && platform.PendingIdleTask(); i++) {
|
||||
platform.PerformIdleTask(kShortIdleTimeInSeconds);
|
||||
}
|
||||
while (platform.PendingIdleTask()) {
|
||||
platform.PerformIdleTask(kLongIdleTimeInSeconds);
|
||||
}
|
||||
CHECK(marking->IsStopped());
|
||||
i::V8::SetPlatformForTesting(old_platform);
|
||||
}
|
||||
|
||||
|
||||
TEST(IncrementalMarkingUsingDelayedTasks) {
|
||||
if (!i::FLAG_incremental_marking) return;
|
||||
CcTest::InitializeVM();
|
||||
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
|
||||
MockPlatform platform(old_platform);
|
||||
i::V8::SetPlatformForTesting(&platform);
|
||||
SimulateFullSpace(CcTest::heap()->old_space());
|
||||
i::IncrementalMarking* marking = CcTest::heap()->incremental_marking();
|
||||
marking->Stop();
|
||||
marking->Start();
|
||||
CHECK(platform.PendingIdleTask());
|
||||
// The delayed task should be a no-op if the idle task makes progress.
|
||||
const int kIgnoredDelayedTaskStepCount = 1000;
|
||||
for (int i = 0; i < kIgnoredDelayedTaskStepCount; i++) {
|
||||
// Dummy idle task progress.
|
||||
marking->incremental_marking_job()->NotifyIdleTaskProgress();
|
||||
CHECK(platform.PendingDelayedTask());
|
||||
platform.PerformDelayedTask();
|
||||
}
|
||||
// Once we stop notifying idle task progress, the delayed tasks
|
||||
// should finish marking.
|
||||
while (!marking->IsStopped() && platform.PendingDelayedTask()) {
|
||||
platform.PerformDelayedTask();
|
||||
}
|
||||
// There could be pending delayed task from memory reducer after GC finishes.
|
||||
CHECK(marking->IsStopped());
|
||||
i::V8::SetPlatformForTesting(old_platform);
|
||||
}
|
@ -23,12 +23,8 @@ class GCIdleTimeHandlerTest : public ::testing::Test {
|
||||
GCIdleTimeHandler::HeapState result;
|
||||
result.contexts_disposed = 0;
|
||||
result.contexts_disposal_rate = GCIdleTimeHandler::kHighContextDisposalRate;
|
||||
result.size_of_objects = kSizeOfObjects;
|
||||
result.incremental_marking_stopped = false;
|
||||
result.sweeping_in_progress = false;
|
||||
result.sweeping_completed = false;
|
||||
result.mark_compact_speed_in_bytes_per_ms = kMarkCompactSpeed;
|
||||
result.incremental_marking_speed_in_bytes_per_ms = kMarkingSpeed;
|
||||
result.scavenge_speed_in_bytes_per_ms = kScavengeSpeed;
|
||||
result.used_new_space_size = 0;
|
||||
result.new_space_capacity = kNewSpaceCapacity;
|
||||
@ -259,10 +255,9 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime1) {
|
||||
heap_state.contexts_disposal_rate =
|
||||
GCIdleTimeHandler::kHighContextDisposalRate;
|
||||
size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
|
||||
double idle_time_ms =
|
||||
static_cast<double>(heap_state.size_of_objects / speed - 1);
|
||||
double idle_time_ms = static_cast<double>(kSizeOfObjects / speed - 1);
|
||||
GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
|
||||
EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
|
||||
EXPECT_EQ(DO_INCREMENTAL_STEP, action.type);
|
||||
}
|
||||
|
||||
|
||||
@ -272,34 +267,17 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime2) {
|
||||
heap_state.contexts_disposal_rate =
|
||||
GCIdleTimeHandler::kHighContextDisposalRate;
|
||||
size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
|
||||
double idle_time_ms =
|
||||
static_cast<double>(heap_state.size_of_objects / speed - 1);
|
||||
double idle_time_ms = static_cast<double>(kSizeOfObjects / speed - 1);
|
||||
GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
|
||||
EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
|
||||
EXPECT_EQ(DO_INCREMENTAL_STEP, action.type);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GCIdleTimeHandlerTest, IncrementalMarking1) {
|
||||
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
|
||||
size_t speed = heap_state.incremental_marking_speed_in_bytes_per_ms;
|
||||
double idle_time_ms = 10;
|
||||
GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
|
||||
EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
|
||||
EXPECT_GT(speed * static_cast<size_t>(idle_time_ms),
|
||||
static_cast<size_t>(action.parameter));
|
||||
EXPECT_LT(0, action.parameter);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GCIdleTimeHandlerTest, IncrementalMarking2) {
|
||||
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
|
||||
size_t speed = heap_state.incremental_marking_speed_in_bytes_per_ms;
|
||||
double idle_time_ms = 10;
|
||||
GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
|
||||
EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
|
||||
EXPECT_GT(speed * static_cast<size_t>(idle_time_ms),
|
||||
static_cast<size_t>(action.parameter));
|
||||
EXPECT_LT(0, action.parameter);
|
||||
EXPECT_EQ(DO_INCREMENTAL_STEP, action.type);
|
||||
}
|
||||
|
||||
|
||||
@ -307,35 +285,12 @@ TEST_F(GCIdleTimeHandlerTest, NotEnoughTime) {
|
||||
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
|
||||
heap_state.incremental_marking_stopped = true;
|
||||
size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
|
||||
double idle_time_ms =
|
||||
static_cast<double>(heap_state.size_of_objects / speed - 1);
|
||||
double idle_time_ms = static_cast<double>(kSizeOfObjects / speed - 1);
|
||||
GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
|
||||
EXPECT_EQ(DONE, action.type);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GCIdleTimeHandlerTest, FinalizeSweeping) {
|
||||
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
|
||||
heap_state.incremental_marking_stopped = true;
|
||||
heap_state.sweeping_in_progress = true;
|
||||
heap_state.sweeping_completed = true;
|
||||
double idle_time_ms = 10.0;
|
||||
GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
|
||||
EXPECT_EQ(DO_FINALIZE_SWEEPING, action.type);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GCIdleTimeHandlerTest, CannotFinalizeSweeping) {
|
||||
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
|
||||
heap_state.incremental_marking_stopped = true;
|
||||
heap_state.sweeping_in_progress = true;
|
||||
heap_state.sweeping_completed = false;
|
||||
double idle_time_ms = 10.0;
|
||||
GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
|
||||
EXPECT_EQ(DO_NOTHING, action.type);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GCIdleTimeHandlerTest, Scavenge) {
|
||||
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
|
||||
int idle_time_ms = 10;
|
||||
@ -382,7 +337,7 @@ TEST_F(GCIdleTimeHandlerTest, ContinueAfterStop) {
|
||||
EXPECT_EQ(DONE, action.type);
|
||||
heap_state.incremental_marking_stopped = false;
|
||||
action = handler()->Compute(idle_time_ms, heap_state);
|
||||
EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
|
||||
EXPECT_EQ(DO_INCREMENTAL_STEP, action.type);
|
||||
}
|
||||
|
||||
|
||||
@ -405,25 +360,6 @@ TEST_F(GCIdleTimeHandlerTest, SmallIdleTimeNothingToDo) {
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GCIdleTimeHandlerTest, DoneIfNotMakingProgressOnSweeping) {
|
||||
// Regression test for crbug.com/489323.
|
||||
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
|
||||
|
||||
// Simulate sweeping being in-progress but not complete.
|
||||
heap_state.incremental_marking_stopped = true;
|
||||
heap_state.sweeping_in_progress = true;
|
||||
heap_state.sweeping_completed = false;
|
||||
double idle_time_ms = 10.0;
|
||||
for (int i = 0; i < GCIdleTimeHandler::kMaxNoProgressIdleTimes; i++) {
|
||||
GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
|
||||
EXPECT_EQ(DO_NOTHING, action.type);
|
||||
}
|
||||
// We should return DONE after not making progress for some time.
|
||||
GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
|
||||
EXPECT_EQ(DONE, action.type);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GCIdleTimeHandlerTest, DoneIfNotMakingProgressOnIncrementalMarking) {
|
||||
// Regression test for crbug.com/489323.
|
||||
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
|
||||
|
@ -713,6 +713,8 @@
|
||||
'../../src/heap/heap.cc',
|
||||
'../../src/heap/heap.h',
|
||||
'../../src/heap/incremental-marking-inl.h',
|
||||
'../../src/heap/incremental-marking-job.cc',
|
||||
'../../src/heap/incremental-marking-job.h',
|
||||
'../../src/heap/incremental-marking.cc',
|
||||
'../../src/heap/incremental-marking.h',
|
||||
'../../src/heap/mark-compact-inl.h',
|
||||
|
Loading…
Reference in New Issue
Block a user