From 6256dc53cc62cd720c3b1869fcb04f1ab2110d62 Mon Sep 17 00:00:00 2001 From: ulan Date: Fri, 25 Sep 2015 07:49:07 -0700 Subject: [PATCH] Perform scavenge in idle tasks. BUG=chromium:490559 LOG=NO Review URL: https://codereview.chromium.org/1352453004 Cr-Commit-Position: refs/heads/master@{#30944} --- BUILD.gn | 2 + src/heap/gc-idle-time-handler.cc | 89 ------------- src/heap/gc-idle-time-handler.h | 28 ----- src/heap/heap.cc | 34 +++-- src/heap/heap.h | 12 ++ src/heap/incremental-marking.cc | 6 +- src/heap/scavenge-job.cc | 116 +++++++++++++++++ src/heap/scavenge-job.h | 80 ++++++++++++ src/heap/spaces.cc | 8 +- test/cctest/cctest.h | 7 ++ test/cctest/test-heap.cc | 2 + test/cctest/test-unboxed-doubles.cc | 1 + .../heap/gc-idle-time-handler-unittest.cc | 119 ------------------ test/unittests/heap/scavenge-job-unittest.cc | 111 ++++++++++++++++ test/unittests/unittests.gyp | 1 + tools/gyp/v8.gyp | 2 + 16 files changed, 367 insertions(+), 251 deletions(-) create mode 100644 src/heap/scavenge-job.cc create mode 100644 src/heap/scavenge-job.h create mode 100644 test/unittests/heap/scavenge-job-unittest.cc diff --git a/BUILD.gn b/BUILD.gn index a67948be1..6f6038475 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -967,6 +967,8 @@ source_set("v8_base") { "src/heap/objects-visiting-inl.h", "src/heap/objects-visiting.cc", "src/heap/objects-visiting.h", + "src/heap/scavenge-job.h", + "src/heap/scavenge-job.cc", "src/heap/scavenger-inl.h", "src/heap/scavenger.cc", "src/heap/scavenger.h", diff --git a/src/heap/gc-idle-time-handler.cc b/src/heap/gc-idle-time-handler.cc index f836a0628..f77c015bf 100644 --- a/src/heap/gc-idle-time-handler.cc +++ b/src/heap/gc-idle-time-handler.cc @@ -32,9 +32,6 @@ void GCIdleTimeAction::Print() { PrintF("; finalized marking"); } break; - case DO_SCAVENGE: - PrintF("scavenge"); - break; case DO_FULL_GC: PrintF("full GC"); break; @@ -53,11 +50,6 @@ void GCIdleTimeHeapState::Print() { mark_compact_speed_in_bytes_per_ms); PrintF("incremental_marking_speed=%" V8_PTR_PREFIX "d ", incremental_marking_speed_in_bytes_per_ms); - PrintF("scavenge_speed=%" V8_PTR_PREFIX "d ", scavenge_speed_in_bytes_per_ms); - PrintF("new_space_size=%" V8_PTR_PREFIX "d ", used_new_space_size); - PrintF("new_space_capacity=%" V8_PTR_PREFIX "d ", new_space_capacity); - PrintF("new_space_allocation_throughput=%" V8_PTR_PREFIX "d ", - new_space_allocation_throughput_in_bytes_per_ms); } @@ -107,79 +99,6 @@ size_t GCIdleTimeHandler::EstimateFinalIncrementalMarkCompactTime( } -bool GCIdleTimeHandler::ShouldDoScavenge( - size_t idle_time_in_ms, size_t new_space_size, size_t used_new_space_size, - size_t scavenge_speed_in_bytes_per_ms, - size_t new_space_allocation_throughput_in_bytes_per_ms) { - if (idle_time_in_ms >= kMinBackgroundIdleTime) { - // It is better to do full GC for the background tab. - return false; - } - - // Calculates how much memory are we able to scavenge in - // kMaxFrameRenderingIdleTime ms. If scavenge_speed_in_bytes_per_ms is 0 we - // will take care of this later. - size_t idle_new_space_allocation_limit = - kMaxFrameRenderingIdleTime * scavenge_speed_in_bytes_per_ms; - - // If the limit is larger than the new space size, then scavenging used to be - // really fast. We can take advantage of the whole new space. - if (idle_new_space_allocation_limit > new_space_size) { - idle_new_space_allocation_limit = new_space_size; - } - - // We do not know the allocation throughput before the first scavenge. - // TODO(hpayer): Estimate allocation throughput before the first scavenge. - if (new_space_allocation_throughput_in_bytes_per_ms > 0) { - // We have to trigger scavenge before we reach the end of new space. - size_t adjust_limit = new_space_allocation_throughput_in_bytes_per_ms * - kTimeUntilNextIdleEvent; - if (adjust_limit > idle_new_space_allocation_limit) { - idle_new_space_allocation_limit = 0; - } else { - idle_new_space_allocation_limit -= adjust_limit; - } - } - - // The allocated new space limit to trigger a scavange has to be at least - // kMinimumNewSpaceSizeToPerformScavenge. - if (idle_new_space_allocation_limit < kMinimumNewSpaceSizeToPerformScavenge) { - idle_new_space_allocation_limit = kMinimumNewSpaceSizeToPerformScavenge; - } - - // Set an initial scavenge speed if it is unknown. - if (scavenge_speed_in_bytes_per_ms == 0) { - scavenge_speed_in_bytes_per_ms = kInitialConservativeScavengeSpeed; - } - - // We apply a max factor to the new space size to make sure that a slowly - // allocating application still leaves enough of wiggle room to schedule a - // scavenge. - size_t max_limit; - const double kMaxNewSpaceSizeFactorLongIdleTimes = 0.5; - const double kMaxNewSpaceSizeFactorShortIdleTimes = 0.8; - if (idle_time_in_ms > kMaxFrameRenderingIdleTime) { - max_limit = static_cast(new_space_size * - kMaxNewSpaceSizeFactorLongIdleTimes); - } else { - max_limit = static_cast(new_space_size * - kMaxNewSpaceSizeFactorShortIdleTimes); - } - idle_new_space_allocation_limit = - Min(idle_new_space_allocation_limit, max_limit); - - // We perform a scavenge if we are over the idle new space limit and - // a scavenge fits into the given idle time bucket. - if (idle_new_space_allocation_limit <= used_new_space_size) { - if (used_new_space_size / scavenge_speed_in_bytes_per_ms <= - idle_time_in_ms) { - return true; - } - } - return false; -} - - bool GCIdleTimeHandler::ShouldDoMarkCompact( size_t idle_time_in_ms, size_t size_of_objects, size_t mark_compact_speed_in_bytes_per_ms) { @@ -259,14 +178,6 @@ GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms, return NothingOrDone(idle_time_in_ms); } - if (ShouldDoScavenge( - static_cast(idle_time_in_ms), heap_state.new_space_capacity, - heap_state.used_new_space_size, - heap_state.scavenge_speed_in_bytes_per_ms, - heap_state.new_space_allocation_throughput_in_bytes_per_ms)) { - return GCIdleTimeAction::Scavenge(); - } - if (!FLAG_incremental_marking || heap_state.incremental_marking_stopped) { return GCIdleTimeAction::Done(); } diff --git a/src/heap/gc-idle-time-handler.h b/src/heap/gc-idle-time-handler.h index ce4aeee9e..36acdd853 100644 --- a/src/heap/gc-idle-time-handler.h +++ b/src/heap/gc-idle-time-handler.h @@ -14,7 +14,6 @@ enum GCIdleTimeActionType { DONE, DO_NOTHING, DO_INCREMENTAL_STEP, - DO_SCAVENGE, DO_FULL_GC, }; @@ -42,13 +41,6 @@ class GCIdleTimeAction { return result; } - static GCIdleTimeAction Scavenge() { - GCIdleTimeAction result; - result.type = DO_SCAVENGE; - result.additional_work = false; - return result; - } - static GCIdleTimeAction FullGC() { GCIdleTimeAction result; result.type = DO_FULL_GC; @@ -77,10 +69,6 @@ class GCIdleTimeHeapState { size_t mark_compact_speed_in_bytes_per_ms; size_t incremental_marking_speed_in_bytes_per_ms; size_t final_incremental_mark_compact_speed_in_bytes_per_ms; - size_t scavenge_speed_in_bytes_per_ms; - size_t used_new_space_size; - size_t new_space_capacity; - size_t new_space_allocation_throughput_in_bytes_per_ms; }; @@ -124,21 +112,10 @@ class GCIdleTimeHandler { static const int kMinBackgroundIdleTime = 900; - // We conservatively assume that in the next kTimeUntilNextIdleEvent ms - // no idle notification happens. - static const size_t kTimeUntilNextIdleEvent = 100; - // An allocation throughput below kLowAllocationThroughput bytes/ms is // considered low static const size_t kLowAllocationThroughput = 1000; - // If we haven't recorded any scavenger events yet, we use a conservative - // lower bound for the scavenger speed. - static const size_t kInitialConservativeScavengeSpeed = 100 * KB; - - // The minimum size of allocated new space objects to trigger a scavenge. - static const size_t kMinimumNewSpaceSizeToPerformScavenge = MB / 2; - // If contexts are disposed at a higher rate a full gc is triggered. static const double kHighContextDisposalRate; @@ -181,11 +158,6 @@ class GCIdleTimeHandler { static bool ShouldDoOverApproximateWeakClosure(size_t idle_time_in_ms); - static bool ShouldDoScavenge( - size_t idle_time_in_ms, size_t new_space_size, size_t used_new_space_size, - size_t scavenger_speed_in_bytes_per_ms, - size_t new_space_allocation_throughput_in_bytes_per_ms); - private: GCIdleTimeAction NothingOrDone(double idle_time_in_ms); diff --git a/src/heap/heap.cc b/src/heap/heap.cc index ae77e1eee..92f5a508e 100644 --- a/src/heap/heap.cc +++ b/src/heap/heap.cc @@ -27,6 +27,7 @@ #include "src/heap/object-stats.h" #include "src/heap/objects-visiting-inl.h" #include "src/heap/objects-visiting.h" +#include "src/heap/scavenge-job.h" #include "src/heap/scavenger-inl.h" #include "src/heap/store-buffer.h" #include "src/heap-profiler.h" @@ -128,6 +129,7 @@ Heap::Heap() gc_idle_time_handler_(nullptr), memory_reducer_(nullptr), object_stats_(nullptr), + scavenge_job_(nullptr), full_codegen_bytes_generated_(0), crankshaft_codegen_bytes_generated_(0), new_space_allocation_counter_(0), @@ -780,6 +782,11 @@ void Heap::HandleGCRequest() { } +void Heap::ScheduleIdleScavengeIfNeeded(int bytes_allocated) { + scavenge_job_->ScheduleIdleTaskIfNeeded(this, bytes_allocated); +} + + void Heap::OverApproximateWeakClosure(const char* gc_reason) { if (FLAG_trace_incremental_marking) { PrintF("[IncrementalMarking] Overapproximate weak closure (%s).\n", @@ -4098,12 +4105,6 @@ GCIdleTimeHeapState Heap::ComputeHeapState() { heap_state.incremental_marking_stopped = incremental_marking()->IsStopped(); heap_state.mark_compact_speed_in_bytes_per_ms = static_cast(tracer()->MarkCompactSpeedInBytesPerMillisecond()); - heap_state.scavenge_speed_in_bytes_per_ms = - static_cast(tracer()->ScavengeSpeedInBytesPerMillisecond()); - heap_state.used_new_space_size = new_space_.Size(); - heap_state.new_space_capacity = new_space_.Capacity(); - heap_state.new_space_allocation_throughput_in_bytes_per_ms = - tracer()->NewSpaceAllocationThroughputInBytesPerMillisecond(); return heap_state; } @@ -4160,9 +4161,6 @@ bool Heap::PerformIdleTimeAction(GCIdleTimeAction action, CollectAllGarbage(kNoGCFlags, "idle notification: contexts disposed"); break; } - case DO_SCAVENGE: - CollectGarbage(NEW_SPACE, "idle notification: scavenge"); - break; case DO_NOTHING: break; } @@ -4989,6 +4987,17 @@ void Heap::DisableInlineAllocation() { } +void Heap::LowerInlineAllocationLimit(intptr_t step) { + new_space()->LowerInlineAllocationLimit(step); +} + + +void Heap::ResetInlineAllocationLimit() { + new_space()->LowerInlineAllocationLimit( + ScavengeJob::kBytesAllocatedBeforeNextIdleTask); +} + + V8_DECLARE_ONCE(initialize_gc_once); static void InitializeGCOnce() { @@ -5081,6 +5090,8 @@ bool Heap::SetUp() { object_stats_ = new ObjectStats(this); object_stats_->ClearObjectStats(true); + scavenge_job_ = new ScavengeJob(); + array_buffer_tracker_ = new ArrayBufferTracker(this); LOG(isolate_, IntPtrTEvent("heap-capacity", Capacity())); @@ -5090,6 +5101,8 @@ bool Heap::SetUp() { mark_compact_collector()->SetUp(); + ResetInlineAllocationLimit(); + return true; } @@ -5202,6 +5215,9 @@ void Heap::TearDown() { delete object_stats_; object_stats_ = nullptr; + delete scavenge_job_; + scavenge_job_ = nullptr; + WaitUntilUnmappingOfFreeChunksCompleted(); delete array_buffer_tracker_; diff --git a/src/heap/heap.h b/src/heap/heap.h index 8fdc6702b..89f126700 100644 --- a/src/heap/heap.h +++ b/src/heap/heap.h @@ -431,6 +431,7 @@ class Isolate; class MemoryReducer; class ObjectStats; class Scavenger; +class ScavengeJob; class WeakObjectRetainer; @@ -1883,11 +1884,19 @@ class Heap { void SetOldGenerationAllocationLimit(intptr_t old_gen_size, double gc_speed, double mutator_speed); + // =========================================================================== + // Inline allocation. ======================================================== + // =========================================================================== + + void LowerInlineAllocationLimit(intptr_t step); + void ResetInlineAllocationLimit(); + // =========================================================================== // Idle notification. ======================================================== // =========================================================================== bool RecentIdleNotificationHappened(); + void ScheduleIdleScavengeIfNeeded(int bytes_allocated); // =========================================================================== // Allocation methods. ======================================================= @@ -2267,6 +2276,8 @@ class Heap { ObjectStats* object_stats_; + ScavengeJob* scavenge_job_; + // These two counters are monotomically increasing and never reset. size_t full_codegen_bytes_generated_; size_t crankshaft_codegen_bytes_generated_; @@ -2340,6 +2351,7 @@ class Heap { friend class IncrementalMarking; friend class MarkCompactCollector; friend class MarkCompactMarkingVisitor; + friend class NewSpace; friend class ObjectStatsVisitor; friend class Page; friend class Scavenger; diff --git a/src/heap/incremental-marking.cc b/src/heap/incremental-marking.cc index 7bf5596d3..f7147b275 100644 --- a/src/heap/incremental-marking.cc +++ b/src/heap/incremental-marking.cc @@ -486,7 +486,7 @@ void IncrementalMarking::Start(const char* reason) { state_ = SWEEPING; } - heap_->new_space()->LowerInlineAllocationLimit(kAllocatedThreshold); + heap_->LowerInlineAllocationLimit(kAllocatedThreshold); incremental_marking_job()->Start(heap_); } @@ -738,7 +738,7 @@ void IncrementalMarking::Stop() { if (FLAG_trace_incremental_marking) { PrintF("[IncrementalMarking] Stopping.\n"); } - heap_->new_space()->LowerInlineAllocationLimit(0); + heap_->ResetInlineAllocationLimit(); IncrementalMarking::set_should_hurry(false); ResetStepCounters(); if (IsMarking()) { @@ -766,7 +766,7 @@ void IncrementalMarking::Finalize() { Hurry(); state_ = STOPPED; is_compacting_ = false; - heap_->new_space()->LowerInlineAllocationLimit(0); + heap_->ResetInlineAllocationLimit(); IncrementalMarking::set_should_hurry(false); ResetStepCounters(); PatchIncrementalMarkingRecordWriteStubs(heap_, diff --git a/src/heap/scavenge-job.cc b/src/heap/scavenge-job.cc new file mode 100644 index 000000000..c3804436f --- /dev/null +++ b/src/heap/scavenge-job.cc @@ -0,0 +1,116 @@ +// 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 "src/heap/scavenge-job.h" + +#include "src/base/platform/time.h" +#include "src/heap/heap-inl.h" +#include "src/heap/heap.h" +#include "src/isolate.h" +#include "src/v8.h" + +namespace v8 { +namespace internal { + + +const double ScavengeJob::kMaxAllocationLimitAsFractionOfNewSpace = 0.8; + +void ScavengeJob::IdleTask::RunInternal(double deadline_in_seconds) { + Heap* heap = isolate_->heap(); + double deadline_in_ms = + deadline_in_seconds * + static_cast(base::Time::kMillisecondsPerSecond); + double start_ms = heap->MonotonicallyIncreasingTimeInMs(); + double idle_time_in_ms = deadline_in_ms - start_ms; + size_t scavenge_speed_in_bytes_per_ms = + static_cast(heap->tracer()->ScavengeSpeedInBytesPerMillisecond()); + size_t new_space_size = heap->new_space()->Size(); + size_t new_space_capacity = heap->new_space()->Capacity(); + + job_->NotifyIdleTask(); + + if (ReachedIdleAllocationLimit(scavenge_speed_in_bytes_per_ms, new_space_size, + new_space_capacity)) { + if (EnoughIdleTimeForScavenge( + idle_time_in_ms, scavenge_speed_in_bytes_per_ms, new_space_size)) { + heap->CollectGarbage(NEW_SPACE, "idle task: scavenge"); + } else { + // Immediately request another idle task that can get larger idle time. + job_->RescheduleIdleTask(heap); + } + } +} + + +bool ScavengeJob::ReachedIdleAllocationLimit( + size_t scavenge_speed_in_bytes_per_ms, size_t new_space_size, + size_t new_space_capacity) { + if (scavenge_speed_in_bytes_per_ms == 0) { + scavenge_speed_in_bytes_per_ms = kInitialScavengeSpeedInBytesPerMs; + } + + // Set the allocation limit to the number of bytes we can scavenge in an + // average idle task. + size_t allocation_limit = kAverageIdleTimeMs * scavenge_speed_in_bytes_per_ms; + + // Keep the limit smaller than the new space capacity. + allocation_limit = + Min(allocation_limit, + static_cast(new_space_capacity * + kMaxAllocationLimitAsFractionOfNewSpace)); + // Adjust the limit to take into account bytes that will be allocated until + // the next check. + allocation_limit = allocation_limit < kBytesAllocatedBeforeNextIdleTask + ? 0 + : allocation_limit - kBytesAllocatedBeforeNextIdleTask; + // Keep the limit large enough to avoid scavenges in tiny new space. + allocation_limit = Max(allocation_limit, kMinAllocationLimit); + + return allocation_limit <= new_space_size; +} + + +bool ScavengeJob::EnoughIdleTimeForScavenge( + double idle_time_in_ms, size_t scavenge_speed_in_bytes_per_ms, + size_t new_space_size) { + if (scavenge_speed_in_bytes_per_ms == 0) { + scavenge_speed_in_bytes_per_ms = kInitialScavengeSpeedInBytesPerMs; + } + return new_space_size <= idle_time_in_ms * scavenge_speed_in_bytes_per_ms; +} + + +void ScavengeJob::RescheduleIdleTask(Heap* heap) { + // Make sure that we don't reschedule more than one time. + // Otherwise, we might spam the scheduler with idle tasks. + if (!idle_task_rescheduled_) { + ScheduleIdleTask(heap); + idle_task_rescheduled_ = true; + } +} + + +void ScavengeJob::ScheduleIdleTaskIfNeeded(Heap* heap, int bytes_allocated) { + bytes_allocated_since_the_last_task_ += bytes_allocated; + if (bytes_allocated_since_the_last_task_ >= + static_cast(kBytesAllocatedBeforeNextIdleTask)) { + ScheduleIdleTask(heap); + bytes_allocated_since_the_last_task_ = 0; + idle_task_rescheduled_ = false; + } +} + + +void ScavengeJob::ScheduleIdleTask(Heap* heap) { + if (!idle_task_pending_) { + v8::Isolate* isolate = reinterpret_cast(heap->isolate()); + if (V8::GetCurrentPlatform()->IdleTasksEnabled(isolate)) { + idle_task_pending_ = true; + auto task = new IdleTask(heap->isolate(), this); + V8::GetCurrentPlatform()->CallIdleOnForegroundThread(isolate, task); + } + } +} +} +} // namespace v8::internal diff --git a/src/heap/scavenge-job.h b/src/heap/scavenge-job.h new file mode 100644 index 000000000..c9e508ec5 --- /dev/null +++ b/src/heap/scavenge-job.h @@ -0,0 +1,80 @@ +// 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. + +#ifndef V8_HEAP_SCAVENGE_JOB_H_ +#define V8_HEAP_SCAVENGE_JOB_H_ + +#include "src/cancelable-task.h" +#include "src/heap/gc-tracer.h" + +namespace v8 { +namespace internal { + +class Heap; +class Isolate; + + +// This class posts idle tasks and performs scavenges in the idle tasks. +class ScavengeJob { + public: + class IdleTask : public CancelableIdleTask { + public: + explicit IdleTask(Isolate* isolate, ScavengeJob* job) + : CancelableIdleTask(isolate), job_(job) {} + // CancelableIdleTask overrides. + void RunInternal(double deadline_in_seconds) override; + + private: + ScavengeJob* job_; + }; + + ScavengeJob() + : idle_task_pending_(false), + idle_task_rescheduled_(false), + bytes_allocated_since_the_last_task_(0) {} + + // Posts an idle task if the cumulative bytes allocated since the last + // idle task exceed kBytesAllocatedBeforeNextIdleTask. + void ScheduleIdleTaskIfNeeded(Heap* heap, int bytes_allocated); + + // Posts an idle task ignoring the bytes allocated, but makes sure + // that the new idle task cannot reschedule again. + // This prevents infinite rescheduling. + void RescheduleIdleTask(Heap* heap); + + bool IdleTaskPending() { return idle_task_pending_; } + void NotifyIdleTask() { idle_task_pending_ = false; } + bool IdleTaskRescheduled() { return idle_task_rescheduled_; } + + static bool ReachedIdleAllocationLimit(size_t scavenge_speed_in_bytes_per_ms, + size_t new_space_size, + size_t new_space_capacity); + + static bool EnoughIdleTimeForScavenge(double idle_time_ms, + size_t scavenge_speed_in_bytes_per_ms, + size_t new_space_size); + + // If we haven't recorded any scavenger events yet, we use a conservative + // lower bound for the scavenger speed. + static const int kInitialScavengeSpeedInBytesPerMs = 256 * KB; + // Estimate of the average idle time that an idle task gets. + static const int kAverageIdleTimeMs = 5; + // The number of bytes to be allocated in new space before the next idle + // task is posted. + static const size_t kBytesAllocatedBeforeNextIdleTask = 512 * KB; + // The minimum size of allocated new space objects to trigger a scavenge. + static const size_t kMinAllocationLimit = 512 * KB; + // The allocation limit cannot exceed this fraction of the new space capacity. + static const double kMaxAllocationLimitAsFractionOfNewSpace; + + private: + void ScheduleIdleTask(Heap* heap); + bool idle_task_pending_; + bool idle_task_rescheduled_; + int bytes_allocated_since_the_last_task_; +}; +} +} // namespace v8::internal + +#endif // V8_HEAP_SCAVENGE_JOB_H_ diff --git a/src/heap/spaces.cc b/src/heap/spaces.cc index b973a4ae6..cd8a72951 100644 --- a/src/heap/spaces.cc +++ b/src/heap/spaces.cc @@ -1410,7 +1410,7 @@ void NewSpace::UpdateInlineAllocationLimit(int size_in_bytes) { Address high = to_space_.page_high(); Address new_top = allocation_info_.top() + size_in_bytes; allocation_info_.set_limit(Min(new_top, high)); - } else if (inline_allocation_limit_step() == 0) { + } else if (inline_allocation_limit_step_ == 0) { // Normal limit is the end of the current page. allocation_info_.set_limit(to_space_.page_high()); } else { @@ -1491,8 +1491,9 @@ bool NewSpace::EnsureAllocation(int size_in_bytes, if (allocation_info_.limit() < high) { // Either the limit has been lowered because linear allocation was disabled - // or because incremental marking wants to get a chance to do a step. Set - // the new limit accordingly. + // or because incremental marking wants to get a chance to do a step, + // or because idle scavenge job wants to get a chance to post a task. + // Set the new limit accordingly. Address new_top = old_top + aligned_size_in_bytes; InlineAllocationStep(new_top, new_top); UpdateInlineAllocationLimit(aligned_size_in_bytes); @@ -1504,6 +1505,7 @@ bool NewSpace::EnsureAllocation(int size_in_bytes, void NewSpace::InlineAllocationStep(Address top, Address new_top) { if (top_on_previous_step_) { int bytes_allocated = static_cast(top - top_on_previous_step_); + heap()->ScheduleIdleScavengeIfNeeded(bytes_allocated); heap()->incremental_marking()->Step(bytes_allocated, IncrementalMarking::GC_VIA_STACK_GUARD); top_on_previous_step_ = new_top; diff --git a/test/cctest/cctest.h b/test/cctest/cctest.h index bbce0adfb..aaa52c8c3 100644 --- a/test/cctest/cctest.h +++ b/test/cctest/cctest.h @@ -525,8 +525,14 @@ static inline void ExpectUndefined(const char* code) { } +static inline void DisableInlineAllocationSteps(v8::internal::NewSpace* space) { + space->LowerInlineAllocationLimit(0); +} + + // Helper function that simulates a full new-space in the heap. static inline bool FillUpOnePage(v8::internal::NewSpace* space) { + DisableInlineAllocationSteps(space); v8::internal::AllocationResult allocation = space->AllocateRawUnaligned( v8::internal::Page::kMaxRegularHeapObjectSize); if (allocation.IsRetry()) return false; @@ -541,6 +547,7 @@ static inline bool FillUpOnePage(v8::internal::NewSpace* space) { // Helper function that simulates a fill new-space in the heap. static inline void AllocateAllButNBytes(v8::internal::NewSpace* space, int extra_bytes) { + DisableInlineAllocationSteps(space); int space_remaining = static_cast(*space->allocation_limit_address() - *space->allocation_top_address()); CHECK(space_remaining >= extra_bytes); diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index 8d7666910..3f6bd05d0 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -1216,6 +1216,7 @@ HEAP_TEST(Regression39128) { Page::kMaxRegularHeapObjectSize + kPointerSize); int allocation_len = LenFromSize(allocation_amount); NewSpace* new_space = heap->new_space(); + DisableInlineAllocationSteps(new_space); Address* top_addr = new_space->allocation_top_address(); Address* limit_addr = new_space->allocation_limit_address(); while ((*limit_addr - *top_addr) > allocation_amount) { @@ -5663,6 +5664,7 @@ UNINITIALIZED_TEST(PromotionQueue) { v8::Context::New(isolate)->Enter(); Heap* heap = i_isolate->heap(); NewSpace* new_space = heap->new_space(); + DisableInlineAllocationSteps(new_space); // In this test we will try to overwrite the promotion queue which is at the // end of to-space. To actually make that possible, we need at least two diff --git a/test/cctest/test-unboxed-doubles.cc b/test/cctest/test-unboxed-doubles.cc index 6a3be9bb9..3299e12c5 100644 --- a/test/cctest/test-unboxed-doubles.cc +++ b/test/cctest/test-unboxed-doubles.cc @@ -1440,6 +1440,7 @@ HEAP_TEST(WriteBarriersInCopyJSObject) { Min(FixedArray::kMaxSize, Page::kMaxRegularHeapObjectSize + kPointerSize); int allocation_len = LenFromSize(allocation_amount); NewSpace* new_space = heap->new_space(); + DisableInlineAllocationSteps(new_space); Address* top_addr = new_space->allocation_top_address(); Address* limit_addr = new_space->allocation_limit_address(); while ((*limit_addr - *top_addr) > allocation_amount) { diff --git a/test/unittests/heap/gc-idle-time-handler-unittest.cc b/test/unittests/heap/gc-idle-time-handler-unittest.cc index cbd5741e1..fad965088 100644 --- a/test/unittests/heap/gc-idle-time-handler-unittest.cc +++ b/test/unittests/heap/gc-idle-time-handler-unittest.cc @@ -25,20 +25,12 @@ class GCIdleTimeHandlerTest : public ::testing::Test { result.contexts_disposal_rate = GCIdleTimeHandler::kHighContextDisposalRate; result.incremental_marking_stopped = false; result.mark_compact_speed_in_bytes_per_ms = kMarkCompactSpeed; - result.scavenge_speed_in_bytes_per_ms = kScavengeSpeed; - result.used_new_space_size = 0; - result.new_space_capacity = kNewSpaceCapacity; - result.new_space_allocation_throughput_in_bytes_per_ms = - kNewSpaceAllocationThroughput; return result; } static const size_t kSizeOfObjects = 100 * MB; static const size_t kMarkCompactSpeed = 200 * KB; static const size_t kMarkingSpeed = 200 * KB; - static const size_t kScavengeSpeed = 100 * KB; - static const size_t kNewSpaceCapacity = 1 * MB; - static const size_t kNewSpaceAllocationThroughput = 10 * KB; static const int kMaxNotifications = 100; private: @@ -107,88 +99,6 @@ TEST(GCIdleTimeHandler, EstimateMarkCompactTimeMax) { } -TEST_F(GCIdleTimeHandlerTest, DoScavengeEmptyNewSpace) { - GCIdleTimeHeapState heap_state = DefaultHeapState(); - int idle_time_ms = 16; - EXPECT_FALSE(GCIdleTimeHandler::ShouldDoScavenge( - idle_time_ms, heap_state.new_space_capacity, - heap_state.used_new_space_size, heap_state.scavenge_speed_in_bytes_per_ms, - heap_state.new_space_allocation_throughput_in_bytes_per_ms)); -} - - -TEST_F(GCIdleTimeHandlerTest, DoScavengeFullNewSpace) { - GCIdleTimeHeapState heap_state = DefaultHeapState(); - heap_state.used_new_space_size = kNewSpaceCapacity; - int idle_time_ms = 16; - EXPECT_TRUE(GCIdleTimeHandler::ShouldDoScavenge( - idle_time_ms, heap_state.new_space_capacity, - heap_state.used_new_space_size, heap_state.scavenge_speed_in_bytes_per_ms, - heap_state.new_space_allocation_throughput_in_bytes_per_ms)); -} - - -TEST_F(GCIdleTimeHandlerTest, DoScavengeUnknownScavengeSpeed) { - GCIdleTimeHeapState heap_state = DefaultHeapState(); - heap_state.used_new_space_size = kNewSpaceCapacity; - heap_state.scavenge_speed_in_bytes_per_ms = 0; - int idle_time_ms = 8; - EXPECT_FALSE(GCIdleTimeHandler::ShouldDoScavenge( - idle_time_ms, heap_state.new_space_capacity, - heap_state.used_new_space_size, heap_state.scavenge_speed_in_bytes_per_ms, - heap_state.new_space_allocation_throughput_in_bytes_per_ms)); -} - - -TEST_F(GCIdleTimeHandlerTest, DoScavengeLowScavengeSpeed) { - GCIdleTimeHeapState heap_state = DefaultHeapState(); - heap_state.used_new_space_size = kNewSpaceCapacity; - heap_state.scavenge_speed_in_bytes_per_ms = 1 * KB; - int idle_time_ms = 16; - EXPECT_FALSE(GCIdleTimeHandler::ShouldDoScavenge( - idle_time_ms, heap_state.new_space_capacity, - heap_state.used_new_space_size, heap_state.scavenge_speed_in_bytes_per_ms, - heap_state.new_space_allocation_throughput_in_bytes_per_ms)); -} - - -TEST_F(GCIdleTimeHandlerTest, DoScavengeLowAllocationRate) { - GCIdleTimeHeapState heap_state = DefaultHeapState(); - heap_state.used_new_space_size = kNewSpaceCapacity; - heap_state.new_space_allocation_throughput_in_bytes_per_ms = - GCIdleTimeHandler::kLowAllocationThroughput - 1; - int idle_time_ms = 16; - EXPECT_TRUE(GCIdleTimeHandler::ShouldDoScavenge( - idle_time_ms, heap_state.new_space_capacity, - heap_state.used_new_space_size, heap_state.scavenge_speed_in_bytes_per_ms, - heap_state.new_space_allocation_throughput_in_bytes_per_ms)); -} - - -TEST_F(GCIdleTimeHandlerTest, DoScavengeHighScavengeSpeed) { - GCIdleTimeHeapState heap_state = DefaultHeapState(); - heap_state.used_new_space_size = kNewSpaceCapacity; - heap_state.scavenge_speed_in_bytes_per_ms = kNewSpaceCapacity; - int idle_time_ms = 16; - EXPECT_TRUE(GCIdleTimeHandler::ShouldDoScavenge( - idle_time_ms, heap_state.new_space_capacity, - heap_state.used_new_space_size, heap_state.scavenge_speed_in_bytes_per_ms, - heap_state.new_space_allocation_throughput_in_bytes_per_ms)); -} - - -TEST_F(GCIdleTimeHandlerTest, DoNotScavengeSmallNewSpaceSize) { - GCIdleTimeHeapState heap_state = DefaultHeapState(); - heap_state.used_new_space_size = (MB / 2) - 1; - heap_state.scavenge_speed_in_bytes_per_ms = kNewSpaceCapacity; - int idle_time_ms = 16; - EXPECT_FALSE(GCIdleTimeHandler::ShouldDoScavenge( - idle_time_ms, heap_state.new_space_capacity, - heap_state.used_new_space_size, heap_state.scavenge_speed_in_bytes_per_ms, - heap_state.new_space_allocation_throughput_in_bytes_per_ms)); -} - - TEST_F(GCIdleTimeHandlerTest, ShouldDoMarkCompact) { size_t idle_time_ms = GCIdleTimeHandler::kMaxScheduledIdleTime; EXPECT_TRUE(GCIdleTimeHandler::ShouldDoMarkCompact(idle_time_ms, 0, 0)); @@ -291,35 +201,6 @@ TEST_F(GCIdleTimeHandlerTest, NotEnoughTime) { } -TEST_F(GCIdleTimeHandlerTest, Scavenge) { - GCIdleTimeHeapState heap_state = DefaultHeapState(); - int idle_time_ms = 10; - heap_state.used_new_space_size = - heap_state.new_space_capacity - - (kNewSpaceAllocationThroughput * idle_time_ms); - GCIdleTimeAction action = - handler()->Compute(static_cast(idle_time_ms), heap_state); - EXPECT_EQ(DO_SCAVENGE, action.type); - heap_state.used_new_space_size = 0; -} - - -TEST_F(GCIdleTimeHandlerTest, ScavengeAndDone) { - GCIdleTimeHeapState heap_state = DefaultHeapState(); - int idle_time_ms = 10; - heap_state.incremental_marking_stopped = true; - heap_state.used_new_space_size = - heap_state.new_space_capacity - - (kNewSpaceAllocationThroughput * idle_time_ms); - GCIdleTimeAction action = - handler()->Compute(static_cast(idle_time_ms), heap_state); - EXPECT_EQ(DO_SCAVENGE, action.type); - heap_state.used_new_space_size = 0; - action = handler()->Compute(static_cast(idle_time_ms), heap_state); - EXPECT_EQ(DONE, action.type); -} - - TEST_F(GCIdleTimeHandlerTest, DoNotStartIncrementalMarking) { GCIdleTimeHeapState heap_state = DefaultHeapState(); heap_state.incremental_marking_stopped = true; diff --git a/test/unittests/heap/scavenge-job-unittest.cc b/test/unittests/heap/scavenge-job-unittest.cc new file mode 100644 index 000000000..dbd463c2d --- /dev/null +++ b/test/unittests/heap/scavenge-job-unittest.cc @@ -0,0 +1,111 @@ +// Copyright 2014 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 + +#include "src/globals.h" +#include "src/heap/scavenge-job.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace v8 { +namespace internal { + +const size_t kScavengeSpeedInBytesPerMs = 500 * KB; +const size_t kNewSpaceCapacity = 8 * MB; + + +TEST(ScavengeJob, AllocationLimitEmptyNewSpace) { + EXPECT_FALSE(ScavengeJob::ReachedIdleAllocationLimit( + kScavengeSpeedInBytesPerMs, 0, kNewSpaceCapacity)); +} + + +TEST(ScavengeJob, AllocationLimitFullNewSpace) { + EXPECT_TRUE(ScavengeJob::ReachedIdleAllocationLimit( + kScavengeSpeedInBytesPerMs, kNewSpaceCapacity, kNewSpaceCapacity)); +} + + +TEST(ScavengeJob, AllocationLimitUnknownScavengeSpeed) { + size_t expected_size = ScavengeJob::kInitialScavengeSpeedInBytesPerMs * + ScavengeJob::kAverageIdleTimeMs - + ScavengeJob::kBytesAllocatedBeforeNextIdleTask; + EXPECT_FALSE(ScavengeJob::ReachedIdleAllocationLimit(0, expected_size - 1, + kNewSpaceCapacity)); + EXPECT_TRUE(ScavengeJob::ReachedIdleAllocationLimit(0, expected_size, + kNewSpaceCapacity)); +} + + +TEST(ScavengeJob, AllocationLimitLowScavengeSpeed) { + size_t scavenge_speed = 1 * KB; + EXPECT_FALSE(ScavengeJob::ReachedIdleAllocationLimit( + scavenge_speed, ScavengeJob::kMinAllocationLimit - 1, kNewSpaceCapacity)); + EXPECT_TRUE(ScavengeJob::ReachedIdleAllocationLimit( + scavenge_speed, ScavengeJob::kMinAllocationLimit, kNewSpaceCapacity)); +} + + +TEST(ScavengeJob, AllocationLimitAverageScavengeSpeed) { + size_t expected_size = + kScavengeSpeedInBytesPerMs * ScavengeJob::kAverageIdleTimeMs - + ScavengeJob::kBytesAllocatedBeforeNextIdleTask; + EXPECT_FALSE(ScavengeJob::ReachedIdleAllocationLimit( + kScavengeSpeedInBytesPerMs, ScavengeJob::kMinAllocationLimit, + kNewSpaceCapacity)); + EXPECT_FALSE(ScavengeJob::ReachedIdleAllocationLimit( + kScavengeSpeedInBytesPerMs, expected_size - 1, kNewSpaceCapacity)); + EXPECT_TRUE(ScavengeJob::ReachedIdleAllocationLimit( + kScavengeSpeedInBytesPerMs, expected_size, kNewSpaceCapacity)); +} + + +TEST(ScavengeJob, AllocationLimitHighScavengeSpeed) { + size_t scavenge_speed = kNewSpaceCapacity; + size_t expected_size = + static_cast( + kNewSpaceCapacity * + ScavengeJob::kMaxAllocationLimitAsFractionOfNewSpace) - + ScavengeJob::kBytesAllocatedBeforeNextIdleTask; + EXPECT_FALSE(ScavengeJob::ReachedIdleAllocationLimit( + scavenge_speed, expected_size - 1, kNewSpaceCapacity)); + EXPECT_TRUE(ScavengeJob::ReachedIdleAllocationLimit( + scavenge_speed, expected_size, kNewSpaceCapacity)); +} + + +TEST(ScavengeJob, EnoughIdleTimeForScavengeUnknownScavengeSpeed) { + size_t scavenge_speed = ScavengeJob::kInitialScavengeSpeedInBytesPerMs; + size_t new_space_size = 1 * MB; + size_t expected_time = (new_space_size + scavenge_speed - 1) / scavenge_speed; + EXPECT_TRUE( + ScavengeJob::EnoughIdleTimeForScavenge(expected_time, 0, new_space_size)); + EXPECT_FALSE(ScavengeJob::EnoughIdleTimeForScavenge(expected_time - 1, 0, + new_space_size)); +} + + +TEST(ScavengeJob, EnoughIdleTimeForScavengeLowScavengeSpeed) { + size_t scavenge_speed = 1 * KB; + size_t new_space_size = 1 * MB; + size_t expected_time = (new_space_size + scavenge_speed - 1) / scavenge_speed; + EXPECT_TRUE(ScavengeJob::EnoughIdleTimeForScavenge( + expected_time, scavenge_speed, new_space_size)); + EXPECT_FALSE(ScavengeJob::EnoughIdleTimeForScavenge( + expected_time - 1, scavenge_speed, new_space_size)); +} + + +TEST(ScavengeJob, EnoughIdleTimeForScavengeHighScavengeSpeed) { + size_t scavenge_speed = kNewSpaceCapacity; + size_t new_space_size = 1 * MB; + size_t expected_time = (new_space_size + scavenge_speed - 1) / scavenge_speed; + EXPECT_TRUE(ScavengeJob::EnoughIdleTimeForScavenge( + expected_time, scavenge_speed, new_space_size)); + EXPECT_FALSE(ScavengeJob::EnoughIdleTimeForScavenge( + expected_time - 1, scavenge_speed, new_space_size)); +} + +} // namespace internal +} // namespace v8 diff --git a/test/unittests/unittests.gyp b/test/unittests/unittests.gyp index 39502c7bf..01b944b23 100644 --- a/test/unittests/unittests.gyp +++ b/test/unittests/unittests.gyp @@ -106,6 +106,7 @@ 'heap/gc-idle-time-handler-unittest.cc', 'heap/memory-reducer-unittest.cc', 'heap/heap-unittest.cc', + 'heap/scavenge-job-unittest.cc', 'run-all-unittests.cc', 'test-utils.h', 'test-utils.cc', diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index c8abd433a..30cdecf19 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -734,6 +734,8 @@ '../../src/heap/objects-visiting-inl.h', '../../src/heap/objects-visiting.cc', '../../src/heap/objects-visiting.h', + '../../src/heap/scavenge-job.h', + '../../src/heap/scavenge-job.cc', '../../src/heap/scavenger-inl.h', '../../src/heap/scavenger.cc', '../../src/heap/scavenger.h', -- 2.34.1