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 3271360 : 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 3271361 : nullptr);
24 3271361 : 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 3271361 : }
28 :
29 3226838 : AccountingAllocator::~AccountingAllocator() { ClearPool(); }
30 :
31 90 : void AccountingAllocator::MemoryPressureNotification(
32 : MemoryPressureLevel level) {
33 : memory_pressure_level_.SetValue(level);
34 :
35 90 : if (level != MemoryPressureLevel::kNone) {
36 85 : ClearPool();
37 : }
38 90 : }
39 :
40 62590 : 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 62590 : size_t fits_fully = max_pool_size / full_size;
45 :
46 62590 : 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 62590 : size_t total_size = fits_fully * full_size;
62 :
63 438130 : for (size_t power = 0; power < kNumberBuckets; ++power) {
64 375540 : if (total_size + (size_t(1) << (power + kMinSegmentSizePower)) <=
65 : max_pool_size) {
66 28779 : unused_segments_max_sizes_[power] = fits_fully + 1;
67 28779 : total_size += size_t(1) << power;
68 : } else {
69 346761 : unused_segments_max_sizes_[power] = fits_fully;
70 : }
71 : }
72 62590 : }
73 :
74 62639144 : Segment* AccountingAllocator::GetSegment(size_t bytes) {
75 62639144 : Segment* result = GetSegmentFromPool(bytes);
76 62649556 : if (result == nullptr) {
77 29297024 : result = AllocateSegment(bytes);
78 29297092 : if (result != nullptr) {
79 : result->Initialize(bytes);
80 : }
81 : }
82 :
83 62649624 : return result;
84 : }
85 :
86 29297013 : Segment* AccountingAllocator::AllocateSegment(size_t bytes) {
87 29297013 : void* memory = AllocWithRetry(bytes);
88 29297087 : if (memory != nullptr) {
89 : base::AtomicWord current =
90 29297091 : base::Relaxed_AtomicIncrement(¤t_memory_usage_, bytes);
91 29297091 : base::AtomicWord max = base::Relaxed_Load(&max_memory_usage_);
92 63824356 : while (current > max) {
93 : max = base::Relaxed_CompareAndSwap(&max_memory_usage_, max, current);
94 : }
95 : }
96 29297087 : return reinterpret_cast<Segment*>(memory);
97 : }
98 :
99 62627618 : void AccountingAllocator::ReturnSegment(Segment* segment) {
100 62627618 : segment->ZapContents();
101 :
102 62626684 : if (memory_pressure_level_.Value() != MemoryPressureLevel::kNone) {
103 2276 : FreeSegment(segment);
104 62624408 : } else if (!AddSegmentToPool(segment)) {
105 27375862 : FreeSegment(segment);
106 : }
107 62634641 : }
108 :
109 29224918 : void AccountingAllocator::FreeSegment(Segment* memory) {
110 : base::Relaxed_AtomicIncrement(¤t_memory_usage_,
111 29224918 : -static_cast<base::AtomicWord>(memory->size()));
112 29224918 : memory->ZapHeader();
113 29224934 : free(memory);
114 29224934 : }
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 62639136 : Segment* AccountingAllocator::GetSegmentFromPool(size_t requested_size) {
129 62639136 : if (requested_size > (1 << kMaxSegmentSizePower)) {
130 : return nullptr;
131 : }
132 :
133 : size_t power = kMinSegmentSizePower;
134 15299245 : while (requested_size > (static_cast<size_t>(1) << power)) power++;
135 :
136 : DCHECK_GE(power, kMinSegmentSizePower + 0);
137 62511928 : power -= kMinSegmentSizePower;
138 :
139 66705862 : Segment* segment;
140 : {
141 62511928 : base::MutexGuard lock_guard(&unused_segments_mutex_);
142 :
143 62522293 : segment = unused_segments_heads_[power];
144 :
145 62522293 : if (segment != nullptr) {
146 33352931 : unused_segments_heads_[power] = segment->next();
147 : segment->set_next(nullptr);
148 :
149 33352931 : unused_segments_sizes_[power]--;
150 : base::Relaxed_AtomicIncrement(
151 33352931 : ¤t_pool_size_, -static_cast<base::AtomicWord>(segment->size()));
152 : }
153 : }
154 :
155 : if (segment) {
156 : DCHECK_GE(segment->size(), requested_size);
157 : }
158 62522018 : return segment;
159 : }
160 :
161 62623881 : bool AccountingAllocator::AddSegmentToPool(Segment* segment) {
162 : size_t size = segment->size();
163 :
164 62623881 : if (size >= (1 << (kMaxSegmentSizePower + 1))) return false;
165 :
166 62544926 : if (size < (1 << kMinSegmentSizePower)) return false;
167 :
168 : size_t power = kMaxSegmentSizePower;
169 :
170 302812430 : while (size < (static_cast<size_t>(1) << power)) power--;
171 :
172 : DCHECK_GE(power, kMinSegmentSizePower + 0);
173 62545314 : power -= kMinSegmentSizePower;
174 :
175 : {
176 62545314 : base::MutexGuard lock_guard(&unused_segments_mutex_);
177 :
178 62553440 : if (unused_segments_sizes_[power] >= unused_segments_max_sizes_[power]) {
179 : return false;
180 : }
181 :
182 35256790 : segment->set_next(unused_segments_heads_[power]);
183 35256790 : unused_segments_heads_[power] = segment;
184 35256790 : base::Relaxed_AtomicIncrement(¤t_pool_size_, size);
185 35256790 : unused_segments_sizes_[power]++;
186 : }
187 :
188 : return true;
189 : }
190 :
191 3226923 : void AccountingAllocator::ClearPool() {
192 3226923 : base::MutexGuard lock_guard(&unused_segments_mutex_);
193 :
194 22588468 : for (size_t power = 0; power <= kMaxSegmentSizePower - kMinSegmentSizePower;
195 : power++) {
196 21208312 : Segment* current = unused_segments_heads_[power];
197 40569856 : while (current) {
198 : Segment* next = current->next();
199 1846768 : FreeSegment(current);
200 : current = next;
201 : }
202 19361544 : unused_segments_heads_[power] = nullptr;
203 : }
204 3226924 : }
205 :
206 : } // namespace internal
207 183867 : } // namespace v8
|