1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/heap/gc-idle-time-handler.h"
6 
7 #include "src/flags.h"
8 #include "src/heap/gc-tracer.h"
9 #include "src/utils.h"
10 
11 namespace v8 {
12 namespace internal {
13 
14 const double GCIdleTimeHandler::kConservativeTimeRatio = 0.9;
15 const size_t GCIdleTimeHandler::kMaxFinalIncrementalMarkCompactTimeInMs = 1000;
16 const double GCIdleTimeHandler::kHighContextDisposalRate = 100;
17 const size_t GCIdleTimeHandler::kMinTimeForOverApproximatingWeakClosureInMs = 1;
18 
19 
Print()20 void GCIdleTimeAction::Print() {
21   switch (type) {
22     case DONE:
23       PrintF("done");
24       break;
25     case DO_NOTHING:
26       PrintF("no action");
27       break;
28     case DO_INCREMENTAL_STEP:
29       PrintF("incremental step");
30       if (additional_work) {
31         PrintF("; finalized marking");
32       }
33       break;
34     case DO_FULL_GC:
35       PrintF("full GC");
36       break;
37   }
38 }
39 
40 
Print()41 void GCIdleTimeHeapState::Print() {
42   PrintF("contexts_disposed=%d ", contexts_disposed);
43   PrintF("contexts_disposal_rate=%f ", contexts_disposal_rate);
44   PrintF("size_of_objects=%" PRIuS " ", size_of_objects);
45   PrintF("incremental_marking_stopped=%d ", incremental_marking_stopped);
46 }
47 
EstimateMarkingStepSize(double idle_time_in_ms,double marking_speed_in_bytes_per_ms)48 size_t GCIdleTimeHandler::EstimateMarkingStepSize(
49     double idle_time_in_ms, double marking_speed_in_bytes_per_ms) {
50   DCHECK(idle_time_in_ms > 0);
51 
52   if (marking_speed_in_bytes_per_ms == 0) {
53     marking_speed_in_bytes_per_ms = kInitialConservativeMarkingSpeed;
54   }
55 
56   double marking_step_size = marking_speed_in_bytes_per_ms * idle_time_in_ms;
57   if (marking_step_size >= kMaximumMarkingStepSize) {
58     return kMaximumMarkingStepSize;
59   }
60   return static_cast<size_t>(marking_step_size * kConservativeTimeRatio);
61 }
62 
EstimateFinalIncrementalMarkCompactTime(size_t size_of_objects,double final_incremental_mark_compact_speed_in_bytes_per_ms)63 double GCIdleTimeHandler::EstimateFinalIncrementalMarkCompactTime(
64     size_t size_of_objects,
65     double final_incremental_mark_compact_speed_in_bytes_per_ms) {
66   if (final_incremental_mark_compact_speed_in_bytes_per_ms == 0) {
67     final_incremental_mark_compact_speed_in_bytes_per_ms =
68         kInitialConservativeFinalIncrementalMarkCompactSpeed;
69   }
70   double result =
71       size_of_objects / final_incremental_mark_compact_speed_in_bytes_per_ms;
72   return Min<double>(result, kMaxFinalIncrementalMarkCompactTimeInMs);
73 }
74 
ShouldDoContextDisposalMarkCompact(int contexts_disposed,double contexts_disposal_rate)75 bool GCIdleTimeHandler::ShouldDoContextDisposalMarkCompact(
76     int contexts_disposed, double contexts_disposal_rate) {
77   return contexts_disposed > 0 && contexts_disposal_rate > 0 &&
78          contexts_disposal_rate < kHighContextDisposalRate;
79 }
80 
ShouldDoFinalIncrementalMarkCompact(double idle_time_in_ms,size_t size_of_objects,double final_incremental_mark_compact_speed_in_bytes_per_ms)81 bool GCIdleTimeHandler::ShouldDoFinalIncrementalMarkCompact(
82     double idle_time_in_ms, size_t size_of_objects,
83     double final_incremental_mark_compact_speed_in_bytes_per_ms) {
84   return idle_time_in_ms >=
85          EstimateFinalIncrementalMarkCompactTime(
86              size_of_objects,
87              final_incremental_mark_compact_speed_in_bytes_per_ms);
88 }
89 
ShouldDoOverApproximateWeakClosure(double idle_time_in_ms)90 bool GCIdleTimeHandler::ShouldDoOverApproximateWeakClosure(
91     double idle_time_in_ms) {
92   // TODO(jochen): Estimate the time it will take to build the object groups.
93   return idle_time_in_ms >= kMinTimeForOverApproximatingWeakClosureInMs;
94 }
95 
96 
NothingOrDone(double idle_time_in_ms)97 GCIdleTimeAction GCIdleTimeHandler::NothingOrDone(double idle_time_in_ms) {
98   if (idle_time_in_ms >= kMinBackgroundIdleTime) {
99     return GCIdleTimeAction::Nothing();
100   }
101   if (idle_times_which_made_no_progress_ >= kMaxNoProgressIdleTimes) {
102     return GCIdleTimeAction::Done();
103   } else {
104     idle_times_which_made_no_progress_++;
105     return GCIdleTimeAction::Nothing();
106   }
107 }
108 
109 
110 // The following logic is implemented by the controller:
111 // (1) If we don't have any idle time, do nothing, unless a context was
112 // disposed, incremental marking is stopped, and the heap is small. Then do
113 // a full GC.
114 // (2) If the context disposal rate is high and we cannot perform a full GC,
115 // we do nothing until the context disposal rate becomes lower.
116 // (3) If the new space is almost full and we can affort a scavenge or if the
117 // next scavenge will very likely take long, then a scavenge is performed.
118 // (4) If sweeping is in progress and we received a large enough idle time
119 // request, we finalize sweeping here.
120 // (5) If incremental marking is in progress, we perform a marking step. Note,
121 // that this currently may trigger a full garbage collection.
Compute(double idle_time_in_ms,GCIdleTimeHeapState heap_state)122 GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms,
123                                             GCIdleTimeHeapState heap_state) {
124   if (static_cast<int>(idle_time_in_ms) <= 0) {
125     if (heap_state.incremental_marking_stopped) {
126       if (ShouldDoContextDisposalMarkCompact(
127               heap_state.contexts_disposed,
128               heap_state.contexts_disposal_rate)) {
129         return GCIdleTimeAction::FullGC();
130       }
131     }
132     return GCIdleTimeAction::Nothing();
133   }
134 
135   // We are in a context disposal GC scenario. Don't do anything if we do not
136   // get the right idle signal.
137   if (ShouldDoContextDisposalMarkCompact(heap_state.contexts_disposed,
138                                          heap_state.contexts_disposal_rate)) {
139     return NothingOrDone(idle_time_in_ms);
140   }
141 
142   if (!FLAG_incremental_marking || heap_state.incremental_marking_stopped) {
143     return GCIdleTimeAction::Done();
144   }
145 
146   return GCIdleTimeAction::IncrementalStep();
147 }
148 
Enabled()149 bool GCIdleTimeHandler::Enabled() { return FLAG_incremental_marking; }
150 
151 }  // namespace internal
152 }  // namespace v8
153