summaryrefslogtreecommitdiff
path: root/deps/v8/src/heap/heap-controller.cc
blob: d59f8abe9f5ccd0c74b7218f7d41067deef5b061 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/heap/heap-controller.h"

#include "src/execution/isolate-inl.h"
#include "src/heap/spaces.h"

namespace v8 {
namespace internal {

template <typename Trait>
double MemoryController<Trait>::GrowingFactor(Heap* heap, size_t max_heap_size,
                                              double gc_speed,
                                              double mutator_speed) {
  const double max_factor = MaxGrowingFactor(max_heap_size);
  const double factor =
      DynamicGrowingFactor(gc_speed, mutator_speed, max_factor);
  if (FLAG_trace_gc_verbose) {
    Isolate::FromHeap(heap)->PrintWithTimestamp(
        "[%s] factor %.1f based on mu=%.3f, speed_ratio=%.f "
        "(gc=%.f, mutator=%.f)\n",
        Trait::kName, factor, Trait::kTargetMutatorUtilization,
        gc_speed / mutator_speed, gc_speed, mutator_speed);
  }
  return factor;
}

template <typename Trait>
double MemoryController<Trait>::MaxGrowingFactor(size_t max_heap_size) {
  constexpr double kMinSmallFactor = 1.3;
  constexpr double kMaxSmallFactor = 2.0;
  constexpr double kHighFactor = 4.0;

  size_t max_size = max_heap_size;
  max_size = Max(max_size, Trait::kMinSize);

  // If we are on a device with lots of memory, we allow a high heap
  // growing factor.
  if (max_size >= Trait::kMaxSize) {
    return kHighFactor;
  }

  DCHECK_GE(max_size, Trait::kMinSize);
  DCHECK_LT(max_size, Trait::kMaxSize);

  // On smaller devices we linearly scale the factor: (X-A)/(B-A)*(D-C)+C
  double factor = (max_size - Trait::kMinSize) *
                      (kMaxSmallFactor - kMinSmallFactor) /
                      (Trait::kMaxSize - Trait::kMinSize) +
                  kMinSmallFactor;
  return factor;
}

// Given GC speed in bytes per ms, the allocation throughput in bytes per ms
// (mutator speed), this function returns the heap growing factor that will
// achieve the target_mutator_utilization_ if the GC speed and the mutator speed
// remain the same until the next GC.
//
// For a fixed time-frame T = TM + TG, the mutator utilization is the ratio
// TM / (TM + TG), where TM is the time spent in the mutator and TG is the
// time spent in the garbage collector.
//
// Let MU be target_mutator_utilization_, the desired mutator utilization for
// the time-frame from the end of the current GC to the end of the next GC.
// Based on the MU we can compute the heap growing factor F as
//
// F = R * (1 - MU) / (R * (1 - MU) - MU), where R = gc_speed / mutator_speed.
//
// This formula can be derived as follows.
//
// F = Limit / Live by definition, where the Limit is the allocation limit,
// and the Live is size of live objects.
// Let’s assume that we already know the Limit. Then:
//   TG = Limit / gc_speed
//   TM = (TM + TG) * MU, by definition of MU.
//   TM = TG * MU / (1 - MU)
//   TM = Limit *  MU / (gc_speed * (1 - MU))
// On the other hand, if the allocation throughput remains constant:
//   Limit = Live + TM * allocation_throughput = Live + TM * mutator_speed
// Solving it for TM, we get
//   TM = (Limit - Live) / mutator_speed
// Combining the two equation for TM:
//   (Limit - Live) / mutator_speed = Limit * MU / (gc_speed * (1 - MU))
//   (Limit - Live) = Limit * MU * mutator_speed / (gc_speed * (1 - MU))
// substitute R = gc_speed / mutator_speed
//   (Limit - Live) = Limit * MU  / (R * (1 - MU))
// substitute F = Limit / Live
//   F - 1 = F * MU  / (R * (1 - MU))
//   F - F * MU / (R * (1 - MU)) = 1
//   F * (1 - MU / (R * (1 - MU))) = 1
//   F * (R * (1 - MU) - MU) / (R * (1 - MU)) = 1
//   F = R * (1 - MU) / (R * (1 - MU) - MU)
template <typename Trait>
double MemoryController<Trait>::DynamicGrowingFactor(double gc_speed,
                                                     double mutator_speed,
                                                     double max_factor) {
  DCHECK_LE(Trait::kMinGrowingFactor, max_factor);
  DCHECK_GE(Trait::kMaxGrowingFactor, max_factor);
  if (gc_speed == 0 || mutator_speed == 0) return max_factor;

  const double speed_ratio = gc_speed / mutator_speed;

  const double a = speed_ratio * (1 - Trait::kTargetMutatorUtilization);
  const double b = speed_ratio * (1 - Trait::kTargetMutatorUtilization) -
                   Trait::kTargetMutatorUtilization;

  // The factor is a / b, but we need to check for small b first.
  double factor = (a < b * max_factor) ? a / b : max_factor;
  factor = Min(factor, max_factor);
  factor = Max(factor, Trait::kMinGrowingFactor);
  return factor;
}

template <typename Trait>
size_t MemoryController<Trait>::MinimumAllocationLimitGrowingStep(
    Heap::HeapGrowingMode growing_mode) {
  const size_t kRegularAllocationLimitGrowingStep = 8;
  const size_t kLowMemoryAllocationLimitGrowingStep = 2;
  size_t limit = (Page::kPageSize > MB ? Page::kPageSize : MB);
  return limit * (growing_mode == Heap::HeapGrowingMode::kConservative
                      ? kLowMemoryAllocationLimitGrowingStep
                      : kRegularAllocationLimitGrowingStep);
}

template <typename Trait>
size_t MemoryController<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) {
  switch (growing_mode) {
    case Heap::HeapGrowingMode::kConservative:
    case Heap::HeapGrowingMode::kSlow:
      factor = Min(factor, Trait::kConservativeGrowingFactor);
      break;
    case Heap::HeapGrowingMode::kMinimal:
      factor = Trait::kMinGrowingFactor;
      break;
    case Heap::HeapGrowingMode::kDefault:
      break;
  }

  if (FLAG_heap_growing_percent > 0) {
    factor = 1.0 + FLAG_heap_growing_percent / 100.0;
  }

  if (FLAG_heap_growing_percent > 0) {
    factor = 1.0 + FLAG_heap_growing_percent / 100.0;
  }

  CHECK_LT(1.0, factor);
  CHECK_LT(0, current_size);
  const uint64_t limit =
      Max(static_cast<uint64_t>(current_size * factor),
          static_cast<uint64_t>(current_size) +
              MinimumAllocationLimitGrowingStep(growing_mode)) +
      new_space_capacity;
  const uint64_t limit_above_min_size = Max<uint64_t>(limit, min_size);
  const uint64_t halfway_to_the_max =
      (static_cast<uint64_t>(current_size) + max_size) / 2;
  const size_t result =
      static_cast<size_t>(Min(limit_above_min_size, halfway_to_the_max));
  if (FLAG_trace_gc_verbose) {
    Isolate::FromHeap(heap)->PrintWithTimestamp(
        "[%s] Limit: old size: %zu KB, new limit: %zu KB (%.1f)\n",
        Trait::kName, current_size / KB, result / KB, factor);
  }
  return result;
}

template class V8_EXPORT_PRIVATE MemoryController<V8HeapTrait>;
template class V8_EXPORT_PRIVATE MemoryController<GlobalMemoryTrait>;

const char* V8HeapTrait::kName = "HeapController";
const char* GlobalMemoryTrait::kName = "GlobalMemoryController";

}  // namespace internal
}  // namespace v8