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