Line data Source code
1 : // Copyright 2016 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/zone/accounting-allocator.h"
6 :
7 : #include <cstdlib>
8 :
9 : #if V8_LIBC_BIONIC
10 : #include <malloc.h> // NOLINT
11 : #endif
12 :
13 : #include "src/allocation.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 :
18 2678730 : AccountingAllocator::AccountingAllocator() : unused_segments_mutex_() {
19 : static const size_t kDefaultBucketMaxSize = 5;
20 :
21 : memory_pressure_level_.SetValue(MemoryPressureLevel::kNone);
22 : std::fill(unused_segments_heads_, unused_segments_heads_ + kNumberBuckets,
23 2678730 : nullptr);
24 2678730 : std::fill(unused_segments_sizes_, unused_segments_sizes_ + kNumberBuckets, 0);
25 : std::fill(unused_segments_max_sizes_,
26 : unused_segments_max_sizes_ + kNumberBuckets, kDefaultBucketMaxSize);
27 2678730 : }
28 :
29 2662932 : AccountingAllocator::~AccountingAllocator() { ClearPool(); }
30 :
31 83 : void AccountingAllocator::MemoryPressureNotification(
32 : MemoryPressureLevel level) {
33 : memory_pressure_level_.SetValue(level);
34 :
35 83 : if (level != MemoryPressureLevel::kNone) {
36 78 : ClearPool();
37 : }
38 83 : }
39 :
40 60751 : void AccountingAllocator::ConfigureSegmentPool(const size_t max_pool_size) {
41 : // The sum of the bytes of one segment of each size.
42 : static const size_t full_size = (size_t(1) << (kMaxSegmentSizePower + 1)) -
43 : (size_t(1) << kMinSegmentSizePower);
44 60751 : size_t fits_fully = max_pool_size / full_size;
45 :
46 60751 : base::MutexGuard lock_guard(&unused_segments_mutex_);
47 :
48 : // We assume few zones (less than 'fits_fully' many) to be active at the same
49 : // time. When zones grow regularly, they will keep requesting segments of
50 : // increasing size each time. Therefore we try to get as many segments with an
51 : // equal number of segments of each size as possible.
52 : // The remaining space is used to make more room for an 'incomplete set' of
53 : // segments beginning with the smaller ones.
54 : // This code will work best if the max_pool_size is a multiple of the
55 : // full_size. If max_pool_size is no sum of segment sizes the actual pool
56 : // size might be smaller then max_pool_size. Note that no actual memory gets
57 : // wasted though.
58 : // TODO(heimbuef): Determine better strategy generating a segment sizes
59 : // distribution that is closer to real/benchmark usecases and uses the given
60 : // max_pool_size more efficiently.
61 60751 : size_t total_size = fits_fully * full_size;
62 :
63 425257 : for (size_t power = 0; power < kNumberBuckets; ++power) {
64 364506 : if (total_size + (size_t(1) << (power + kMinSegmentSizePower)) <=
65 : max_pool_size) {
66 29552 : unused_segments_max_sizes_[power] = fits_fully + 1;
67 29552 : total_size += size_t(1) << power;
68 : } else {
69 334954 : unused_segments_max_sizes_[power] = fits_fully;
70 : }
71 : }
72 60751 : }
73 :
74 52052928 : Segment* AccountingAllocator::GetSegment(size_t bytes) {
75 52052928 : Segment* result = GetSegmentFromPool(bytes);
76 52060082 : if (result == nullptr) {
77 26190242 : result = AllocateSegment(bytes);
78 26190230 : if (result != nullptr) {
79 : result->Initialize(bytes);
80 : }
81 : }
82 :
83 52060070 : return result;
84 : }
85 :
86 26190229 : Segment* AccountingAllocator::AllocateSegment(size_t bytes) {
87 26190229 : void* memory = AllocWithRetry(bytes);
88 26190203 : if (memory != nullptr) {
89 : base::AtomicWord current =
90 26190223 : base::Relaxed_AtomicIncrement(¤t_memory_usage_, bytes);
91 26190223 : base::AtomicWord max = base::Relaxed_Load(&max_memory_usage_);
92 56834728 : while (current > max) {
93 : max = base::Relaxed_CompareAndSwap(&max_memory_usage_, max, current);
94 : }
95 : }
96 26190203 : return reinterpret_cast<Segment*>(memory);
97 : }
98 :
99 52040825 : void AccountingAllocator::ReturnSegment(Segment* segment) {
100 52040825 : segment->ZapContents();
101 :
102 52040630 : if (memory_pressure_level_.Value() != MemoryPressureLevel::kNone) {
103 2236 : FreeSegment(segment);
104 52038394 : } else if (!AddSegmentToPool(segment)) {
105 24617684 : FreeSegment(segment);
106 : }
107 52045415 : }
108 :
109 26175223 : void AccountingAllocator::FreeSegment(Segment* memory) {
110 : base::Relaxed_AtomicIncrement(¤t_memory_usage_,
111 26175223 : -static_cast<base::AtomicWord>(memory->size()));
112 26175223 : memory->ZapHeader();
113 26175232 : free(memory);
114 26175232 : }
115 :
116 507 : size_t AccountingAllocator::GetCurrentMemoryUsage() const {
117 1014 : return base::Relaxed_Load(¤t_memory_usage_);
118 : }
119 :
120 507 : size_t AccountingAllocator::GetMaxMemoryUsage() const {
121 1014 : return base::Relaxed_Load(&max_memory_usage_);
122 : }
123 :
124 0 : size_t AccountingAllocator::GetCurrentPoolSize() const {
125 0 : return base::Relaxed_Load(¤t_pool_size_);
126 : }
127 :
128 52052952 : Segment* AccountingAllocator::GetSegmentFromPool(size_t requested_size) {
129 52052952 : if (requested_size > (1 << kMaxSegmentSizePower)) {
130 : return nullptr;
131 : }
132 :
133 : size_t power = kMinSegmentSizePower;
134 15356217 : while (requested_size > (static_cast<size_t>(1) << power)) power++;
135 :
136 : DCHECK_GE(power, kMinSegmentSizePower + 0);
137 51910752 : power -= kMinSegmentSizePower;
138 :
139 51740018 : Segment* segment;
140 : {
141 51910752 : base::MutexGuard lock_guard(&unused_segments_mutex_);
142 :
143 51917798 : segment = unused_segments_heads_[power];
144 :
145 51917798 : if (segment != nullptr) {
146 25870009 : unused_segments_heads_[power] = segment->next();
147 : segment->set_next(nullptr);
148 :
149 25870009 : unused_segments_sizes_[power]--;
150 : base::Relaxed_AtomicIncrement(
151 25870009 : ¤t_pool_size_, -static_cast<base::AtomicWord>(segment->size()));
152 : }
153 : }
154 :
155 : if (segment) {
156 : DCHECK_GE(segment->size(), requested_size);
157 : }
158 51917701 : return segment;
159 : }
160 :
161 52038106 : bool AccountingAllocator::AddSegmentToPool(Segment* segment) {
162 : size_t size = segment->size();
163 :
164 52038106 : if (size >= (1 << (kMaxSegmentSizePower + 1))) return false;
165 :
166 51946719 : if (size < (1 << kMinSegmentSizePower)) return false;
167 :
168 : size_t power = kMaxSegmentSizePower;
169 :
170 249746170 : while (size < (static_cast<size_t>(1) << power)) power--;
171 :
172 : DCHECK_GE(power, kMinSegmentSizePower + 0);
173 51946996 : power -= kMinSegmentSizePower;
174 :
175 : {
176 51946996 : base::MutexGuard lock_guard(&unused_segments_mutex_);
177 :
178 51951743 : if (unused_segments_sizes_[power] >= unused_segments_max_sizes_[power]) {
179 : return false;
180 : }
181 :
182 27425651 : segment->set_next(unused_segments_heads_[power]);
183 27425651 : unused_segments_heads_[power] = segment;
184 27425651 : base::Relaxed_AtomicIncrement(¤t_pool_size_, size);
185 27425651 : unused_segments_sizes_[power]++;
186 : }
187 :
188 27425586 : return true;
189 : }
190 :
191 2663010 : void AccountingAllocator::ClearPool() {
192 2663010 : base::MutexGuard lock_guard(&unused_segments_mutex_);
193 :
194 18641077 : for (size_t power = 0; power <= kMaxSegmentSizePower - kMinSegmentSizePower;
195 : power++) {
196 17533368 : Segment* current = unused_segments_heads_[power];
197 33511434 : while (current) {
198 : Segment* next = current->next();
199 1555302 : FreeSegment(current);
200 : current = next;
201 : }
202 15978066 : unused_segments_heads_[power] = nullptr;
203 : }
204 2663011 : }
205 :
206 : } // namespace internal
207 178779 : } // namespace v8
|