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 559906 : 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 559906 : nullptr);
22 559906 : 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 559906 : }
26 :
27 544420 : AccountingAllocator::~AccountingAllocator() { ClearPool(); }
28 :
29 22 : void AccountingAllocator::MemoryPressureNotification(
30 : MemoryPressureLevel level) {
31 : memory_pressure_level_.SetValue(level);
32 :
33 22 : if (level != MemoryPressureLevel::kNone) {
34 17 : ClearPool();
35 : }
36 22 : }
37 :
38 54821 : 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 54821 : size_t fits_fully = max_pool_size / full_size;
43 :
44 54821 : 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 54821 : size_t total_size = fits_fully * full_size;
60 :
61 383747 : for (size_t power = 0; power < kNumberBuckets; ++power) {
62 328926 : if (total_size + (size_t(1) << (power + kMinSegmentSizePower)) <=
63 : max_pool_size) {
64 26789 : unused_segments_max_sizes_[power] = fits_fully + 1;
65 26789 : total_size += size_t(1) << power;
66 : } else {
67 302137 : unused_segments_max_sizes_[power] = fits_fully;
68 : }
69 : }
70 54821 : }
71 :
72 42777833 : Segment* AccountingAllocator::GetSegment(size_t bytes) {
73 42777833 : Segment* result = GetSegmentFromPool(bytes);
74 42779949 : if (result == nullptr) {
75 20831621 : result = AllocateSegment(bytes);
76 20831596 : if (result != nullptr) {
77 : result->Initialize(bytes);
78 : }
79 : }
80 :
81 42779924 : return result;
82 : }
83 :
84 20831608 : Segment* AccountingAllocator::AllocateSegment(size_t bytes) {
85 20831608 : void* memory = malloc(bytes);
86 20831608 : if (memory == nullptr) {
87 6 : V8::GetCurrentPlatform()->OnCriticalMemoryPressure();
88 6 : memory = malloc(bytes);
89 : }
90 20831608 : if (memory != nullptr) {
91 : base::AtomicWord current =
92 20831590 : base::Relaxed_AtomicIncrement(¤t_memory_usage_, bytes);
93 20831590 : base::AtomicWord max = base::Relaxed_Load(&max_memory_usage_);
94 43944924 : while (current > max) {
95 : max = base::Relaxed_CompareAndSwap(&max_memory_usage_, max, current);
96 : }
97 : }
98 20831608 : return reinterpret_cast<Segment*>(memory);
99 : }
100 :
101 42762822 : void AccountingAllocator::ReturnSegment(Segment* segment) {
102 42762822 : segment->ZapContents();
103 :
104 42762854 : if (memory_pressure_level_.Value() != MemoryPressureLevel::kNone) {
105 98 : FreeSegment(segment);
106 42762756 : } else if (!AddSegmentToPool(segment)) {
107 20321014 : FreeSegment(segment);
108 : }
109 42764375 : }
110 :
111 20816036 : void AccountingAllocator::FreeSegment(Segment* memory) {
112 : base::Relaxed_AtomicIncrement(¤t_memory_usage_,
113 20816036 : -static_cast<base::AtomicWord>(memory->size()));
114 20816036 : memory->ZapHeader();
115 20816065 : free(memory);
116 20816065 : }
117 :
118 24 : size_t AccountingAllocator::GetCurrentMemoryUsage() const {
119 48 : return base::Relaxed_Load(¤t_memory_usage_);
120 : }
121 :
122 24 : size_t AccountingAllocator::GetMaxMemoryUsage() const {
123 48 : return base::Relaxed_Load(&max_memory_usage_);
124 : }
125 :
126 0 : size_t AccountingAllocator::GetCurrentPoolSize() const {
127 0 : return base::Relaxed_Load(¤t_pool_size_);
128 : }
129 :
130 42777822 : Segment* AccountingAllocator::GetSegmentFromPool(size_t requested_size) {
131 42777822 : if (requested_size > (1 << kMaxSegmentSizePower)) {
132 : return nullptr;
133 : }
134 :
135 : size_t power = kMinSegmentSizePower;
136 11795745 : while (requested_size > (static_cast<size_t>(1) << power)) power++;
137 :
138 : DCHECK_GE(power, kMinSegmentSizePower + 0);
139 42700298 : power -= kMinSegmentSizePower;
140 :
141 43896864 : Segment* segment;
142 : {
143 42700298 : base::LockGuard<base::Mutex> lock_guard(&unused_segments_mutex_);
144 :
145 42702387 : segment = unused_segments_heads_[power];
146 :
147 42702387 : if (segment != nullptr) {
148 21948432 : unused_segments_heads_[power] = segment->next();
149 : segment->set_next(nullptr);
150 :
151 21948432 : unused_segments_sizes_[power]--;
152 : base::Relaxed_AtomicIncrement(
153 21948432 : ¤t_pool_size_, -static_cast<base::AtomicWord>(segment->size()));
154 : }
155 : }
156 :
157 : if (segment) {
158 : DCHECK_GE(segment->size(), requested_size);
159 : }
160 42702264 : return segment;
161 : }
162 :
163 42762726 : bool AccountingAllocator::AddSegmentToPool(Segment* segment) {
164 : size_t size = segment->size();
165 :
166 42762726 : if (size >= (1 << (kMaxSegmentSizePower + 1))) return false;
167 :
168 42734158 : if (size < (1 << kMinSegmentSizePower)) return false;
169 :
170 : size_t power = kMaxSegmentSizePower;
171 :
172 206522114 : while (size < (static_cast<size_t>(1) << power)) power--;
173 :
174 : DCHECK_GE(power, kMinSegmentSizePower + 0);
175 42734240 : power -= kMinSegmentSizePower;
176 :
177 : {
178 42734240 : base::LockGuard<base::Mutex> lock_guard(&unused_segments_mutex_);
179 :
180 42735803 : if (unused_segments_sizes_[power] >= unused_segments_max_sizes_[power]) {
181 : return false;
182 : }
183 :
184 22443366 : segment->set_next(unused_segments_heads_[power]);
185 22443366 : unused_segments_heads_[power] = segment;
186 22443366 : base::Relaxed_AtomicIncrement(¤t_pool_size_, size);
187 22443366 : unused_segments_sizes_[power]++;
188 : }
189 :
190 22443362 : return true;
191 : }
192 :
193 544437 : void AccountingAllocator::ClearPool() {
194 544437 : base::LockGuard<base::Mutex> lock_guard(&unused_segments_mutex_);
195 :
196 3811059 : for (size_t power = 0; power <= kMaxSegmentSizePower - kMinSegmentSizePower;
197 : power++) {
198 3761556 : Segment* current = unused_segments_heads_[power];
199 7028178 : while (current) {
200 : Segment* next = current->next();
201 494934 : FreeSegment(current);
202 : current = next;
203 : }
204 3266622 : unused_segments_heads_[power] = nullptr;
205 : }
206 544437 : }
207 :
208 : } // namespace internal
209 : } // namespace v8
|