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 : namespace v8 {
14 : namespace internal {
15 :
16 631991 : AccountingAllocator::AccountingAllocator() : unused_segments_mutex_() {
17 : static const size_t kDefaultBucketMaxSize = 5;
18 :
19 : memory_pressure_level_.SetValue(MemoryPressureLevel::kNone);
20 : std::fill(unused_segments_heads_, unused_segments_heads_ + kNumberBuckets,
21 631991 : nullptr);
22 631991 : std::fill(unused_segments_sizes_, unused_segments_sizes_ + kNumberBuckets, 0);
23 : std::fill(unused_segments_max_sizes_,
24 : unused_segments_max_sizes_ + kNumberBuckets, kDefaultBucketMaxSize);
25 631991 : }
26 :
27 614976 : AccountingAllocator::~AccountingAllocator() { ClearPool(); }
28 :
29 26 : void AccountingAllocator::MemoryPressureNotification(
30 : MemoryPressureLevel level) {
31 : memory_pressure_level_.SetValue(level);
32 :
33 26 : if (level != MemoryPressureLevel::kNone) {
34 20 : ClearPool();
35 : }
36 26 : }
37 :
38 60607 : void AccountingAllocator::ConfigureSegmentPool(const size_t max_pool_size) {
39 : // The sum of the bytes of one segment of each size.
40 : static const size_t full_size = (size_t(1) << (kMaxSegmentSizePower + 1)) -
41 : (size_t(1) << kMinSegmentSizePower);
42 60607 : size_t fits_fully = max_pool_size / full_size;
43 :
44 60607 : base::LockGuard<base::Mutex> lock_guard(&unused_segments_mutex_);
45 :
46 : // We assume few zones (less than 'fits_fully' many) to be active at the same
47 : // time. When zones grow regularly, they will keep requesting segments of
48 : // increasing size each time. Therefore we try to get as many segments with an
49 : // equal number of segments of each size as possible.
50 : // The remaining space is used to make more room for an 'incomplete set' of
51 : // segments beginning with the smaller ones.
52 : // This code will work best if the max_pool_size is a multiple of the
53 : // full_size. If max_pool_size is no sum of segment sizes the actual pool
54 : // size might be smaller then max_pool_size. Note that no actual memory gets
55 : // wasted though.
56 : // TODO(heimbuef): Determine better strategy generating a segment sizes
57 : // distribution that is closer to real/benchmark usecases and uses the given
58 : // max_pool_size more efficiently.
59 60607 : size_t total_size = fits_fully * full_size;
60 :
61 424249 : for (size_t power = 0; power < kNumberBuckets; ++power) {
62 363642 : if (total_size + (size_t(1) << (power + kMinSegmentSizePower)) <=
63 : max_pool_size) {
64 28627 : unused_segments_max_sizes_[power] = fits_fully + 1;
65 28627 : total_size += size_t(1) << power;
66 : } else {
67 335015 : unused_segments_max_sizes_[power] = fits_fully;
68 : }
69 : }
70 60607 : }
71 :
72 44982049 : Segment* AccountingAllocator::GetSegment(size_t bytes) {
73 44982049 : Segment* result = GetSegmentFromPool(bytes);
74 44984321 : if (result == nullptr) {
75 20351754 : result = AllocateSegment(bytes);
76 20351484 : if (result != nullptr) {
77 : result->Initialize(bytes);
78 : }
79 : }
80 :
81 44984051 : return result;
82 : }
83 :
84 20351754 : Segment* AccountingAllocator::AllocateSegment(size_t bytes) {
85 20351754 : void* memory = malloc(bytes);
86 20351754 : if (memory) {
87 : base::AtomicWord current =
88 20351725 : base::NoBarrier_AtomicIncrement(¤t_memory_usage_, bytes);
89 20351725 : base::AtomicWord max = base::NoBarrier_Load(&max_memory_usage_);
90 42984457 : while (current > max) {
91 : max = base::NoBarrier_CompareAndSwap(&max_memory_usage_, max, current);
92 : }
93 : }
94 20351754 : return reinterpret_cast<Segment*>(memory);
95 : }
96 :
97 44964481 : void AccountingAllocator::ReturnSegment(Segment* segment) {
98 44964481 : segment->ZapContents();
99 :
100 44964456 : if (memory_pressure_level_.Value() != MemoryPressureLevel::kNone) {
101 137 : FreeSegment(segment);
102 44964319 : } else if (!AddSegmentToPool(segment)) {
103 19754917 : FreeSegment(segment);
104 : }
105 44967182 : }
106 :
107 20334010 : void AccountingAllocator::FreeSegment(Segment* memory) {
108 : base::NoBarrier_AtomicIncrement(
109 20334010 : ¤t_memory_usage_, -static_cast<base::AtomicWord>(memory->size()));
110 20334010 : memory->ZapHeader();
111 20334054 : free(memory);
112 20334054 : }
113 :
114 7 : size_t AccountingAllocator::GetCurrentMemoryUsage() const {
115 14 : return base::NoBarrier_Load(¤t_memory_usage_);
116 : }
117 :
118 7 : size_t AccountingAllocator::GetMaxMemoryUsage() const {
119 14 : return base::NoBarrier_Load(&max_memory_usage_);
120 : }
121 :
122 0 : size_t AccountingAllocator::GetCurrentPoolSize() const {
123 0 : return base::NoBarrier_Load(¤t_pool_size_);
124 : }
125 :
126 44982065 : Segment* AccountingAllocator::GetSegmentFromPool(size_t requested_size) {
127 44982065 : if (requested_size > (1 << kMaxSegmentSizePower)) {
128 : return nullptr;
129 : }
130 :
131 : size_t power = kMinSegmentSizePower;
132 13994128 : while (requested_size > (static_cast<size_t>(1) << power)) power++;
133 :
134 : DCHECK_GE(power, kMinSegmentSizePower + 0);
135 44851366 : power -= kMinSegmentSizePower;
136 :
137 49265308 : Segment* segment;
138 : {
139 44851366 : base::LockGuard<base::Mutex> lock_guard(&unused_segments_mutex_);
140 :
141 44853663 : segment = unused_segments_heads_[power];
142 :
143 44853663 : if (segment != nullptr) {
144 24632654 : unused_segments_heads_[power] = segment->next();
145 : segment->set_next(nullptr);
146 :
147 24632654 : unused_segments_sizes_[power]--;
148 : base::NoBarrier_AtomicIncrement(
149 24632654 : ¤t_pool_size_, -static_cast<base::AtomicWord>(segment->size()));
150 : }
151 : }
152 :
153 : if (segment) {
154 : DCHECK_GE(segment->size(), requested_size);
155 : }
156 44853552 : return segment;
157 : }
158 :
159 44964160 : bool AccountingAllocator::AddSegmentToPool(Segment* segment) {
160 : size_t size = segment->size();
161 :
162 44964160 : if (size >= (1 << (kMaxSegmentSizePower + 1))) return false;
163 :
164 44911817 : if (size < (1 << kMinSegmentSizePower)) return false;
165 :
166 : size_t power = kMaxSegmentSizePower;
167 :
168 215886345 : while (size < (static_cast<size_t>(1) << power)) power--;
169 :
170 : DCHECK_GE(power, kMinSegmentSizePower + 0);
171 44911983 : power -= kMinSegmentSizePower;
172 :
173 : {
174 44911983 : base::LockGuard<base::Mutex> lock_guard(&unused_segments_mutex_);
175 :
176 44914820 : if (unused_segments_sizes_[power] >= unused_segments_max_sizes_[power]) {
177 : return false;
178 : }
179 :
180 25212232 : segment->set_next(unused_segments_heads_[power]);
181 25212232 : unused_segments_heads_[power] = segment;
182 25212232 : base::NoBarrier_AtomicIncrement(¤t_pool_size_, size);
183 25212232 : unused_segments_sizes_[power]++;
184 : }
185 :
186 25212232 : return true;
187 : }
188 :
189 614996 : void AccountingAllocator::ClearPool() {
190 614996 : base::LockGuard<base::Mutex> lock_guard(&unused_segments_mutex_);
191 :
192 4304978 : for (size_t power = 0; power <= kMaxSegmentSizePower - kMinSegmentSizePower;
193 : power++) {
194 4268935 : Segment* current = unused_segments_heads_[power];
195 7958917 : while (current) {
196 : Segment* next = current->next();
197 578954 : FreeSegment(current);
198 : current = next;
199 : }
200 3689982 : unused_segments_heads_[power] = nullptr;
201 : }
202 614997 : }
203 :
204 : } // namespace internal
205 : } // namespace v8
|