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::kMaxMarkCompactTimeInMs = 1000;
16 const size_t GCIdleTimeHandler::kMaxFinalIncrementalMarkCompactTimeInMs = 1000;
17 const double GCIdleTimeHandler::kHighContextDisposalRate = 100;
18 const size_t GCIdleTimeHandler::kMinTimeForOverApproximatingWeakClosureInMs = 1;
19 
20 
Print()21 void GCIdleTimeAction::Print() {
22   switch (type) {
23     case DONE:
24       PrintF("done");
25       break;
26     case DO_NOTHING:
27       PrintF("no action");
28       break;
29     case DO_INCREMENTAL_STEP:
30       PrintF("incremental step");
31       if (additional_work) {
32         PrintF("; finalized marking");
33       }
34       break;
35     case DO_FULL_GC:
36       PrintF("full GC");
37       break;
38   }
39 }
40 
41 
Print()42 void GCIdleTimeHeapState::Print() {
43   PrintF("contexts_disposed=%d ", contexts_disposed);
44   PrintF("contexts_disposal_rate=%f ", contexts_disposal_rate);
45   PrintF("size_of_objects=%" V8_PTR_PREFIX "d ", size_of_objects);
46   PrintF("incremental_marking_stopped=%d ", incremental_marking_stopped);
47 }
48 
49 
EstimateMarkingStepSize(size_t idle_time_in_ms,size_t marking_speed_in_bytes_per_ms)50 size_t GCIdleTimeHandler::EstimateMarkingStepSize(
51     size_t idle_time_in_ms, size_t marking_speed_in_bytes_per_ms) {
52   DCHECK(idle_time_in_ms > 0);
53 
54   if (marking_speed_in_bytes_per_ms == 0) {
55     marking_speed_in_bytes_per_ms = kInitialConservativeMarkingSpeed;
56   }
57 
58   size_t marking_step_size = marking_speed_in_bytes_per_ms * idle_time_in_ms;
59   if (marking_step_size / marking_speed_in_bytes_per_ms != idle_time_in_ms) {
60     // In the case of an overflow we return maximum marking step size.
61     return kMaximumMarkingStepSize;
62   }
63 
64   if (marking_step_size > kMaximumMarkingStepSize)
65     return kMaximumMarkingStepSize;
66 
67   return static_cast<size_t>(marking_step_size * kConservativeTimeRatio);
68 }
69 
70 
EstimateMarkCompactTime(size_t size_of_objects,size_t mark_compact_speed_in_bytes_per_ms)71 size_t GCIdleTimeHandler::EstimateMarkCompactTime(
72     size_t size_of_objects, size_t mark_compact_speed_in_bytes_per_ms) {
73   // TODO(hpayer): Be more precise about the type of mark-compact event. It
74   // makes a huge difference if compaction is happening.
75   if (mark_compact_speed_in_bytes_per_ms == 0) {
76     mark_compact_speed_in_bytes_per_ms = kInitialConservativeMarkCompactSpeed;
77   }
78   size_t result = size_of_objects / mark_compact_speed_in_bytes_per_ms;
79   return Min(result, kMaxMarkCompactTimeInMs);
80 }
81 
82 
EstimateFinalIncrementalMarkCompactTime(size_t size_of_objects,size_t final_incremental_mark_compact_speed_in_bytes_per_ms)83 size_t GCIdleTimeHandler::EstimateFinalIncrementalMarkCompactTime(
84     size_t size_of_objects,
85     size_t final_incremental_mark_compact_speed_in_bytes_per_ms) {
86   if (final_incremental_mark_compact_speed_in_bytes_per_ms == 0) {
87     final_incremental_mark_compact_speed_in_bytes_per_ms =
88         kInitialConservativeFinalIncrementalMarkCompactSpeed;
89   }
90   size_t result =
91       size_of_objects / final_incremental_mark_compact_speed_in_bytes_per_ms;
92   return Min(result, kMaxFinalIncrementalMarkCompactTimeInMs);
93 }
94 
95 
ShouldDoMarkCompact(size_t idle_time_in_ms,size_t size_of_objects,size_t mark_compact_speed_in_bytes_per_ms)96 bool GCIdleTimeHandler::ShouldDoMarkCompact(
97     size_t idle_time_in_ms, size_t size_of_objects,
98     size_t mark_compact_speed_in_bytes_per_ms) {
99   return idle_time_in_ms >= kMaxScheduledIdleTime &&
100          idle_time_in_ms >=
101              EstimateMarkCompactTime(size_of_objects,
102                                      mark_compact_speed_in_bytes_per_ms);
103 }
104 
105 
ShouldDoContextDisposalMarkCompact(int contexts_disposed,double contexts_disposal_rate)106 bool GCIdleTimeHandler::ShouldDoContextDisposalMarkCompact(
107     int contexts_disposed, double contexts_disposal_rate) {
108   return contexts_disposed > 0 && contexts_disposal_rate > 0 &&
109          contexts_disposal_rate < kHighContextDisposalRate;
110 }
111 
112 
ShouldDoFinalIncrementalMarkCompact(size_t idle_time_in_ms,size_t size_of_objects,size_t final_incremental_mark_compact_speed_in_bytes_per_ms)113 bool GCIdleTimeHandler::ShouldDoFinalIncrementalMarkCompact(
114     size_t idle_time_in_ms, size_t size_of_objects,
115     size_t final_incremental_mark_compact_speed_in_bytes_per_ms) {
116   return idle_time_in_ms >=
117          EstimateFinalIncrementalMarkCompactTime(
118              size_of_objects,
119              final_incremental_mark_compact_speed_in_bytes_per_ms);
120 }
121 
122 
ShouldDoOverApproximateWeakClosure(size_t idle_time_in_ms)123 bool GCIdleTimeHandler::ShouldDoOverApproximateWeakClosure(
124     size_t idle_time_in_ms) {
125   // TODO(jochen): Estimate the time it will take to build the object groups.
126   return idle_time_in_ms >= kMinTimeForOverApproximatingWeakClosureInMs;
127 }
128 
129 
NothingOrDone(double idle_time_in_ms)130 GCIdleTimeAction GCIdleTimeHandler::NothingOrDone(double idle_time_in_ms) {
131   if (idle_time_in_ms >= kMinBackgroundIdleTime) {
132     return GCIdleTimeAction::Nothing();
133   }
134   if (idle_times_which_made_no_progress_ >= kMaxNoProgressIdleTimes) {
135     return GCIdleTimeAction::Done();
136   } else {
137     idle_times_which_made_no_progress_++;
138     return GCIdleTimeAction::Nothing();
139   }
140 }
141 
142 
143 // The following logic is implemented by the controller:
144 // (1) If we don't have any idle time, do nothing, unless a context was
145 // disposed, incremental marking is stopped, and the heap is small. Then do
146 // a full GC.
147 // (2) If the context disposal rate is high and we cannot perform a full GC,
148 // we do nothing until the context disposal rate becomes lower.
149 // (3) If the new space is almost full and we can affort a scavenge or if the
150 // next scavenge will very likely take long, then a scavenge is performed.
151 // (4) If sweeping is in progress and we received a large enough idle time
152 // request, we finalize sweeping here.
153 // (5) If incremental marking is in progress, we perform a marking step. Note,
154 // that this currently may trigger a full garbage collection.
Compute(double idle_time_in_ms,GCIdleTimeHeapState heap_state)155 GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms,
156                                             GCIdleTimeHeapState heap_state) {
157   if (static_cast<int>(idle_time_in_ms) <= 0) {
158     if (heap_state.incremental_marking_stopped) {
159       if (ShouldDoContextDisposalMarkCompact(
160               heap_state.contexts_disposed,
161               heap_state.contexts_disposal_rate)) {
162         return GCIdleTimeAction::FullGC();
163       }
164     }
165     return GCIdleTimeAction::Nothing();
166   }
167 
168   // We are in a context disposal GC scenario. Don't do anything if we do not
169   // get the right idle signal.
170   if (ShouldDoContextDisposalMarkCompact(heap_state.contexts_disposed,
171                                          heap_state.contexts_disposal_rate)) {
172     return NothingOrDone(idle_time_in_ms);
173   }
174 
175   if (!FLAG_incremental_marking || heap_state.incremental_marking_stopped) {
176     return GCIdleTimeAction::Done();
177   }
178 
179   return GCIdleTimeAction::IncrementalStep();
180 }
181 
182 
183 }  // namespace internal
184 }  // namespace v8
185