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 <limits>
6
7#include "src/heap/gc-idle-time-handler.h"
8#include "testing/gtest/include/gtest/gtest.h"
9
10namespace v8 {
11namespace internal {
12
13namespace {
14
15class GCIdleTimeHandlerTest : public ::testing::Test {
16 public:
17  GCIdleTimeHandlerTest() {}
18  virtual ~GCIdleTimeHandlerTest() {}
19
20  GCIdleTimeHandler* handler() { return &handler_; }
21
22  GCIdleTimeHandler::HeapState DefaultHeapState() {
23    GCIdleTimeHandler::HeapState result;
24    result.contexts_disposed = 0;
25    result.size_of_objects = kSizeOfObjects;
26    result.incremental_marking_stopped = false;
27    result.can_start_incremental_marking = true;
28    result.sweeping_in_progress = false;
29    result.mark_compact_speed_in_bytes_per_ms = kMarkCompactSpeed;
30    result.incremental_marking_speed_in_bytes_per_ms = kMarkingSpeed;
31    result.scavenge_speed_in_bytes_per_ms = kScavengeSpeed;
32    result.available_new_space_memory = kNewSpaceCapacity;
33    result.new_space_capacity = kNewSpaceCapacity;
34    result.new_space_allocation_throughput_in_bytes_per_ms =
35        kNewSpaceAllocationThroughput;
36    return result;
37  }
38
39  static const size_t kSizeOfObjects = 100 * MB;
40  static const size_t kMarkCompactSpeed = 200 * KB;
41  static const size_t kMarkingSpeed = 200 * KB;
42  static const size_t kScavengeSpeed = 100 * KB;
43  static const size_t kNewSpaceCapacity = 1 * MB;
44  static const size_t kNewSpaceAllocationThroughput = 10 * KB;
45
46 private:
47  GCIdleTimeHandler handler_;
48};
49
50}  // namespace
51
52
53TEST(GCIdleTimeHandler, EstimateMarkingStepSizeInitial) {
54  size_t step_size = GCIdleTimeHandler::EstimateMarkingStepSize(1, 0);
55  EXPECT_EQ(
56      static_cast<size_t>(GCIdleTimeHandler::kInitialConservativeMarkingSpeed *
57                          GCIdleTimeHandler::kConservativeTimeRatio),
58      step_size);
59}
60
61
62TEST(GCIdleTimeHandler, EstimateMarkingStepSizeNonZero) {
63  size_t marking_speed_in_bytes_per_millisecond = 100;
64  size_t step_size = GCIdleTimeHandler::EstimateMarkingStepSize(
65      1, marking_speed_in_bytes_per_millisecond);
66  EXPECT_EQ(static_cast<size_t>(marking_speed_in_bytes_per_millisecond *
67                                GCIdleTimeHandler::kConservativeTimeRatio),
68            step_size);
69}
70
71
72TEST(GCIdleTimeHandler, EstimateMarkingStepSizeOverflow1) {
73  size_t step_size = GCIdleTimeHandler::EstimateMarkingStepSize(
74      10, std::numeric_limits<size_t>::max());
75  EXPECT_EQ(static_cast<size_t>(GCIdleTimeHandler::kMaximumMarkingStepSize),
76            step_size);
77}
78
79
80TEST(GCIdleTimeHandler, EstimateMarkingStepSizeOverflow2) {
81  size_t step_size = GCIdleTimeHandler::EstimateMarkingStepSize(
82      std::numeric_limits<size_t>::max(), 10);
83  EXPECT_EQ(static_cast<size_t>(GCIdleTimeHandler::kMaximumMarkingStepSize),
84            step_size);
85}
86
87
88TEST(GCIdleTimeHandler, EstimateMarkCompactTimeInitial) {
89  size_t size = 100 * MB;
90  size_t time = GCIdleTimeHandler::EstimateMarkCompactTime(size, 0);
91  EXPECT_EQ(size / GCIdleTimeHandler::kInitialConservativeMarkCompactSpeed,
92            time);
93}
94
95
96TEST(GCIdleTimeHandler, EstimateMarkCompactTimeNonZero) {
97  size_t size = 100 * MB;
98  size_t speed = 1 * MB;
99  size_t time = GCIdleTimeHandler::EstimateMarkCompactTime(size, speed);
100  EXPECT_EQ(size / speed, time);
101}
102
103
104TEST(GCIdleTimeHandler, EstimateMarkCompactTimeMax) {
105  size_t size = std::numeric_limits<size_t>::max();
106  size_t speed = 1;
107  size_t time = GCIdleTimeHandler::EstimateMarkCompactTime(size, speed);
108  EXPECT_EQ(GCIdleTimeHandler::kMaxMarkCompactTimeInMs, time);
109}
110
111
112TEST(GCIdleTimeHandler, EstimateScavengeTimeInitial) {
113  size_t size = 1 * MB;
114  size_t time = GCIdleTimeHandler::EstimateScavengeTime(size, 0);
115  EXPECT_EQ(size / GCIdleTimeHandler::kInitialConservativeScavengeSpeed, time);
116}
117
118
119TEST(GCIdleTimeHandler, EstimateScavengeTimeNonZero) {
120  size_t size = 1 * MB;
121  size_t speed = 1 * MB;
122  size_t time = GCIdleTimeHandler::EstimateScavengeTime(size, speed);
123  EXPECT_EQ(size / speed, time);
124}
125
126
127TEST(GCIdleTimeHandler, ScavangeMayHappenSoonInitial) {
128  size_t available = 100 * KB;
129  EXPECT_FALSE(GCIdleTimeHandler::ScavangeMayHappenSoon(available, 0));
130}
131
132
133TEST(GCIdleTimeHandler, ScavangeMayHappenSoonNonZeroFalse) {
134  size_t available = (GCIdleTimeHandler::kMaxFrameRenderingIdleTime + 1) * KB;
135  size_t speed = 1 * KB;
136  EXPECT_FALSE(GCIdleTimeHandler::ScavangeMayHappenSoon(available, speed));
137}
138
139
140TEST(GCIdleTimeHandler, ScavangeMayHappenSoonNonZeroTrue) {
141  size_t available = GCIdleTimeHandler::kMaxFrameRenderingIdleTime * KB;
142  size_t speed = 1 * KB;
143  EXPECT_TRUE(GCIdleTimeHandler::ScavangeMayHappenSoon(available, speed));
144}
145
146
147TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeLargeIdleTime) {
148  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
149  heap_state.contexts_disposed = 1;
150  heap_state.incremental_marking_stopped = true;
151  size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
152  int idle_time_ms =
153      static_cast<int>((heap_state.size_of_objects + speed - 1) / speed);
154  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
155  EXPECT_EQ(DO_FULL_GC, action.type);
156}
157
158
159TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime1) {
160  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
161  heap_state.contexts_disposed = 1;
162  heap_state.incremental_marking_stopped = true;
163  size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
164  int idle_time_ms = static_cast<int>(heap_state.size_of_objects / speed - 1);
165  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
166  EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
167}
168
169
170TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime2) {
171  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
172  heap_state.contexts_disposed = 1;
173  size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
174  int idle_time_ms = static_cast<int>(heap_state.size_of_objects / speed - 1);
175  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
176  EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
177}
178
179
180TEST_F(GCIdleTimeHandlerTest, IncrementalMarking1) {
181  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
182  size_t speed = heap_state.incremental_marking_speed_in_bytes_per_ms;
183  int idle_time_ms = 10;
184  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
185  EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
186  EXPECT_GT(speed * static_cast<size_t>(idle_time_ms),
187            static_cast<size_t>(action.parameter));
188  EXPECT_LT(0, action.parameter);
189}
190
191
192TEST_F(GCIdleTimeHandlerTest, IncrementalMarking2) {
193  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
194  heap_state.incremental_marking_stopped = true;
195  size_t speed = heap_state.incremental_marking_speed_in_bytes_per_ms;
196  int idle_time_ms = 10;
197  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
198  EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
199  EXPECT_GT(speed * static_cast<size_t>(idle_time_ms),
200            static_cast<size_t>(action.parameter));
201  EXPECT_LT(0, action.parameter);
202}
203
204
205TEST_F(GCIdleTimeHandlerTest, NotEnoughTime) {
206  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
207  heap_state.incremental_marking_stopped = true;
208  heap_state.can_start_incremental_marking = false;
209  size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
210  int idle_time_ms = static_cast<int>(heap_state.size_of_objects / speed - 1);
211  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
212  EXPECT_EQ(DO_NOTHING, action.type);
213}
214
215
216TEST_F(GCIdleTimeHandlerTest, StopEventually1) {
217  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
218  heap_state.incremental_marking_stopped = true;
219  heap_state.can_start_incremental_marking = false;
220  size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
221  int idle_time_ms = static_cast<int>(heap_state.size_of_objects / speed + 1);
222  for (int i = 0; i < GCIdleTimeHandler::kMaxMarkCompactsInIdleRound; i++) {
223    GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
224    EXPECT_EQ(DO_FULL_GC, action.type);
225    handler()->NotifyIdleMarkCompact();
226  }
227  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
228  EXPECT_EQ(DONE, action.type);
229}
230
231
232TEST_F(GCIdleTimeHandlerTest, StopEventually2) {
233  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
234  int idle_time_ms = 10;
235  for (int i = 0; i < GCIdleTimeHandler::kMaxMarkCompactsInIdleRound; i++) {
236    GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
237    EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
238    // In this case we emulate incremental marking steps that finish with a
239    // full gc.
240    handler()->NotifyIdleMarkCompact();
241  }
242  heap_state.can_start_incremental_marking = false;
243  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
244  EXPECT_EQ(DONE, action.type);
245}
246
247
248TEST_F(GCIdleTimeHandlerTest, ContinueAfterStop1) {
249  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
250  heap_state.incremental_marking_stopped = true;
251  heap_state.can_start_incremental_marking = false;
252  size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
253  int idle_time_ms = static_cast<int>(heap_state.size_of_objects / speed + 1);
254  for (int i = 0; i < GCIdleTimeHandler::kMaxMarkCompactsInIdleRound; i++) {
255    GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
256    EXPECT_EQ(DO_FULL_GC, action.type);
257    handler()->NotifyIdleMarkCompact();
258  }
259  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
260  EXPECT_EQ(DONE, action.type);
261  // Emulate mutator work.
262  for (int i = 0; i < GCIdleTimeHandler::kIdleScavengeThreshold; i++) {
263    handler()->NotifyScavenge();
264  }
265  action = handler()->Compute(idle_time_ms, heap_state);
266  EXPECT_EQ(DO_FULL_GC, action.type);
267}
268
269
270TEST_F(GCIdleTimeHandlerTest, ContinueAfterStop2) {
271  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
272  int idle_time_ms = 10;
273  for (int i = 0; i < GCIdleTimeHandler::kMaxMarkCompactsInIdleRound; i++) {
274    GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
275    if (action.type == DONE) break;
276    EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
277    // In this case we try to emulate incremental marking steps the finish with
278    // a full gc.
279    handler()->NotifyIdleMarkCompact();
280  }
281  heap_state.can_start_incremental_marking = false;
282  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
283  EXPECT_EQ(DONE, action.type);
284  // Emulate mutator work.
285  for (int i = 0; i < GCIdleTimeHandler::kIdleScavengeThreshold; i++) {
286    handler()->NotifyScavenge();
287  }
288  heap_state.can_start_incremental_marking = true;
289  action = handler()->Compute(idle_time_ms, heap_state);
290  EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
291}
292
293
294TEST_F(GCIdleTimeHandlerTest, Scavenge) {
295  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
296  int idle_time_ms = 10;
297  heap_state.available_new_space_memory =
298      kNewSpaceAllocationThroughput * idle_time_ms;
299  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
300  EXPECT_EQ(DO_SCAVENGE, action.type);
301}
302
303
304TEST_F(GCIdleTimeHandlerTest, ScavengeAndDone) {
305  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
306  int idle_time_ms = 10;
307  heap_state.can_start_incremental_marking = false;
308  heap_state.incremental_marking_stopped = true;
309  heap_state.available_new_space_memory =
310      kNewSpaceAllocationThroughput * idle_time_ms;
311  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
312  EXPECT_EQ(DO_SCAVENGE, action.type);
313  heap_state.available_new_space_memory = kNewSpaceCapacity;
314  action = handler()->Compute(idle_time_ms, heap_state);
315  EXPECT_EQ(DO_NOTHING, action.type);
316}
317
318
319TEST_F(GCIdleTimeHandlerTest, ZeroIdleTimeNothingToDo) {
320  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
321  int idle_time_ms = 0;
322  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
323  EXPECT_EQ(DO_NOTHING, action.type);
324}
325
326
327TEST_F(GCIdleTimeHandlerTest, ZeroIdleTimeDoNothingButStartIdleRound) {
328  GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
329  int idle_time_ms = 10;
330  for (int i = 0; i < GCIdleTimeHandler::kMaxMarkCompactsInIdleRound; i++) {
331    GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
332    if (action.type == DONE) break;
333    EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type);
334    // In this case we try to emulate incremental marking steps the finish with
335    // a full gc.
336    handler()->NotifyIdleMarkCompact();
337  }
338  GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
339  // Emulate mutator work.
340  for (int i = 0; i < GCIdleTimeHandler::kIdleScavengeThreshold; i++) {
341    handler()->NotifyScavenge();
342  }
343  action = handler()->Compute(0, heap_state);
344  EXPECT_EQ(DO_NOTHING, action.type);
345}
346
347}  // namespace internal
348}  // namespace v8
349