• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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