1 // Copyright 2012 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/heap-controller.h"
6
7 #include "src/execution/isolate-inl.h"
8 #include "src/heap/spaces.h"
9
10 namespace v8 {
11 namespace internal {
12
13 template <typename Trait>
GrowingFactor(Heap * heap,size_t max_heap_size,double gc_speed,double mutator_speed)14 double MemoryController<Trait>::GrowingFactor(Heap* heap, size_t max_heap_size,
15 double gc_speed,
16 double mutator_speed) {
17 const double max_factor = MaxGrowingFactor(max_heap_size);
18 const double factor =
19 DynamicGrowingFactor(gc_speed, mutator_speed, max_factor);
20 if (FLAG_trace_gc_verbose) {
21 Isolate::FromHeap(heap)->PrintWithTimestamp(
22 "[%s] factor %.1f based on mu=%.3f, speed_ratio=%.f "
23 "(gc=%.f, mutator=%.f)\n",
24 Trait::kName, factor, Trait::kTargetMutatorUtilization,
25 gc_speed / mutator_speed, gc_speed, mutator_speed);
26 }
27 return factor;
28 }
29
30 template <typename Trait>
MaxGrowingFactor(size_t max_heap_size)31 double MemoryController<Trait>::MaxGrowingFactor(size_t max_heap_size) {
32 constexpr double kMinSmallFactor = 1.3;
33 constexpr double kMaxSmallFactor = 2.0;
34 constexpr double kHighFactor = 4.0;
35
36 size_t max_size = max_heap_size;
37 max_size = Max(max_size, Trait::kMinSize);
38
39 // If we are on a device with lots of memory, we allow a high heap
40 // growing factor.
41 if (max_size >= Trait::kMaxSize) {
42 return kHighFactor;
43 }
44
45 DCHECK_GE(max_size, Trait::kMinSize);
46 DCHECK_LT(max_size, Trait::kMaxSize);
47
48 // On smaller devices we linearly scale the factor: (X-A)/(B-A)*(D-C)+C
49 double factor = (max_size - Trait::kMinSize) *
50 (kMaxSmallFactor - kMinSmallFactor) /
51 (Trait::kMaxSize - Trait::kMinSize) +
52 kMinSmallFactor;
53 return factor;
54 }
55
56 // Given GC speed in bytes per ms, the allocation throughput in bytes per ms
57 // (mutator speed), this function returns the heap growing factor that will
58 // achieve the target_mutator_utilization_ if the GC speed and the mutator speed
59 // remain the same until the next GC.
60 //
61 // For a fixed time-frame T = TM + TG, the mutator utilization is the ratio
62 // TM / (TM + TG), where TM is the time spent in the mutator and TG is the
63 // time spent in the garbage collector.
64 //
65 // Let MU be target_mutator_utilization_, the desired mutator utilization for
66 // the time-frame from the end of the current GC to the end of the next GC.
67 // Based on the MU we can compute the heap growing factor F as
68 //
69 // F = R * (1 - MU) / (R * (1 - MU) - MU), where R = gc_speed / mutator_speed.
70 //
71 // This formula can be derived as follows.
72 //
73 // F = Limit / Live by definition, where the Limit is the allocation limit,
74 // and the Live is size of live objects.
75 // Let’s assume that we already know the Limit. Then:
76 // TG = Limit / gc_speed
77 // TM = (TM + TG) * MU, by definition of MU.
78 // TM = TG * MU / (1 - MU)
79 // TM = Limit * MU / (gc_speed * (1 - MU))
80 // On the other hand, if the allocation throughput remains constant:
81 // Limit = Live + TM * allocation_throughput = Live + TM * mutator_speed
82 // Solving it for TM, we get
83 // TM = (Limit - Live) / mutator_speed
84 // Combining the two equation for TM:
85 // (Limit - Live) / mutator_speed = Limit * MU / (gc_speed * (1 - MU))
86 // (Limit - Live) = Limit * MU * mutator_speed / (gc_speed * (1 - MU))
87 // substitute R = gc_speed / mutator_speed
88 // (Limit - Live) = Limit * MU / (R * (1 - MU))
89 // substitute F = Limit / Live
90 // F - 1 = F * MU / (R * (1 - MU))
91 // F - F * MU / (R * (1 - MU)) = 1
92 // F * (1 - MU / (R * (1 - MU))) = 1
93 // F * (R * (1 - MU) - MU) / (R * (1 - MU)) = 1
94 // F = R * (1 - MU) / (R * (1 - MU) - MU)
95 template <typename Trait>
DynamicGrowingFactor(double gc_speed,double mutator_speed,double max_factor)96 double MemoryController<Trait>::DynamicGrowingFactor(double gc_speed,
97 double mutator_speed,
98 double max_factor) {
99 DCHECK_LE(Trait::kMinGrowingFactor, max_factor);
100 DCHECK_GE(Trait::kMaxGrowingFactor, max_factor);
101 if (gc_speed == 0 || mutator_speed == 0) return max_factor;
102
103 const double speed_ratio = gc_speed / mutator_speed;
104
105 const double a = speed_ratio * (1 - Trait::kTargetMutatorUtilization);
106 const double b = speed_ratio * (1 - Trait::kTargetMutatorUtilization) -
107 Trait::kTargetMutatorUtilization;
108
109 // The factor is a / b, but we need to check for small b first.
110 double factor = (a < b * max_factor) ? a / b : max_factor;
111 factor = Min(factor, max_factor);
112 factor = Max(factor, Trait::kMinGrowingFactor);
113 return factor;
114 }
115
116 template <typename Trait>
MinimumAllocationLimitGrowingStep(Heap::HeapGrowingMode growing_mode)117 size_t MemoryController<Trait>::MinimumAllocationLimitGrowingStep(
118 Heap::HeapGrowingMode growing_mode) {
119 const size_t kRegularAllocationLimitGrowingStep = 8;
120 const size_t kLowMemoryAllocationLimitGrowingStep = 2;
121 size_t limit = (Page::kPageSize > MB ? Page::kPageSize : MB);
122 return limit * (growing_mode == Heap::HeapGrowingMode::kConservative
123 ? kLowMemoryAllocationLimitGrowingStep
124 : kRegularAllocationLimitGrowingStep);
125 }
126
127 template <typename Trait>
CalculateAllocationLimit(Heap * heap,size_t current_size,size_t min_size,size_t max_size,size_t new_space_capacity,double factor,Heap::HeapGrowingMode growing_mode)128 size_t MemoryController<Trait>::CalculateAllocationLimit(
129 Heap* heap, size_t current_size, size_t min_size, size_t max_size,
130 size_t new_space_capacity, double factor,
131 Heap::HeapGrowingMode growing_mode) {
132 switch (growing_mode) {
133 case Heap::HeapGrowingMode::kConservative:
134 case Heap::HeapGrowingMode::kSlow:
135 factor = Min(factor, Trait::kConservativeGrowingFactor);
136 break;
137 case Heap::HeapGrowingMode::kMinimal:
138 factor = Trait::kMinGrowingFactor;
139 break;
140 case Heap::HeapGrowingMode::kDefault:
141 break;
142 }
143
144 if (FLAG_heap_growing_percent > 0) {
145 factor = 1.0 + FLAG_heap_growing_percent / 100.0;
146 }
147
148 if (FLAG_heap_growing_percent > 0) {
149 factor = 1.0 + FLAG_heap_growing_percent / 100.0;
150 }
151
152 CHECK_LT(1.0, factor);
153 CHECK_LT(0, current_size);
154 const uint64_t limit =
155 Max(static_cast<uint64_t>(current_size * factor),
156 static_cast<uint64_t>(current_size) +
157 MinimumAllocationLimitGrowingStep(growing_mode)) +
158 new_space_capacity;
159 const uint64_t limit_above_min_size = Max<uint64_t>(limit, min_size);
160 const uint64_t halfway_to_the_max =
161 (static_cast<uint64_t>(current_size) + max_size) / 2;
162 const size_t result =
163 static_cast<size_t>(Min(limit_above_min_size, halfway_to_the_max));
164 if (FLAG_trace_gc_verbose) {
165 Isolate::FromHeap(heap)->PrintWithTimestamp(
166 "[%s] Limit: old size: %zu KB, new limit: %zu KB (%.1f)\n",
167 Trait::kName, current_size / KB, result / KB, factor);
168 }
169 return result;
170 }
171
172 template class V8_EXPORT_PRIVATE MemoryController<V8HeapTrait>;
173 template class V8_EXPORT_PRIVATE MemoryController<GlobalMemoryTrait>;
174
175 const char* V8HeapTrait::kName = "HeapController";
176 const char* GlobalMemoryTrait::kName = "GlobalMemoryController";
177
178 } // namespace internal
179 } // namespace v8
180