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 10 namespace v8 { 11 namespace internal { 12 13 namespace { 14 15 class 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 53 TEST(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 62 TEST(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 72 TEST(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 80 TEST(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 88 TEST(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 96 TEST(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 104 TEST(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 112 TEST(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 119 TEST(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 127 TEST(GCIdleTimeHandler, ScavangeMayHappenSoonInitial) { 128 size_t available = 100 * KB; 129 EXPECT_FALSE(GCIdleTimeHandler::ScavangeMayHappenSoon(available, 0)); 130 } 131 132 133 TEST(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 140 TEST(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 147 TEST_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 159 TEST_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 170 TEST_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 180 TEST_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 192 TEST_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 205 TEST_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 216 TEST_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 232 TEST_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 248 TEST_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 270 TEST_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 294 TEST_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 304 TEST_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 319 TEST_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 327 TEST_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