Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(99)

Side by Side Diff: courgette/memory_allocator.h

Issue 6677141: Switch out use of std::string and std::vector for large allocations for a buffer class that doesn... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: fix linux build error. remove unnecessary call Created 9 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « courgette/ensemble_create.cc ('k') | courgette/memory_allocator.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #ifndef COURGETTE_MEMORY_ALLOCATOR_H_ 5 #ifndef COURGETTE_MEMORY_ALLOCATOR_H_
6 #define COURGETTE_MEMORY_ALLOCATOR_H_ 6 #define COURGETTE_MEMORY_ALLOCATOR_H_
7 7
8 #include <memory> 8 #include <memory>
9 9
10 #include "base/basictypes.h" 10 #include "base/basictypes.h"
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
59 59
60 // Manages a temporary file. The file is created in the %TEMP% folder and 60 // Manages a temporary file. The file is created in the %TEMP% folder and
61 // is deleted when the file handle is closed. 61 // is deleted when the file handle is closed.
62 // NOTE: Since the file will be used as backing for a memory allocation, 62 // NOTE: Since the file will be used as backing for a memory allocation,
63 // it will never be so big that size_t cannot represent its size. 63 // it will never be so big that size_t cannot represent its size.
64 class TempFile { 64 class TempFile {
65 public: 65 public:
66 TempFile(); 66 TempFile();
67 ~TempFile(); 67 ~TempFile();
68 68
69 __declspec(noinline) void Create(); 69 bool Create();
70 void Close(); 70 void Close();
71 __declspec(noinline) void SetSize(size_t size); 71 bool SetSize(size_t size);
72 72
73 // Returns true iff the temp file is currently open. 73 // Returns true iff the temp file is currently open.
74 bool valid() const; 74 bool valid() const;
75 75
76 // Returns the handle of the temporary file or INVALID_HANDLE_VALUE if 76 // Returns the handle of the temporary file or INVALID_HANDLE_VALUE if
77 // a temp file has not been created. 77 // a temp file has not been created.
78 base::PlatformFile handle() const; 78 base::PlatformFile handle() const;
79 79
80 // Returns the size of the temp file. If the temp file doesn't exist,
81 // the return value is 0.
82 size_t size() const;
83
84 protected: 80 protected:
85 __declspec(noinline) FilePath PrepareTempFile();
86
87 base::PlatformFile file_; 81 base::PlatformFile file_;
88 size_t size_;
89 }; 82 };
90 83
91 // Manages a read/write virtual mapping of a physical file. 84 // Manages a read/write virtual mapping of a physical file.
92 class FileMapping { 85 class FileMapping {
93 public: 86 public:
94 FileMapping(); 87 FileMapping();
95 ~FileMapping(); 88 ~FileMapping();
96 89
97 // Map a file from beginning to |size|. 90 // Map a file from beginning to |size|.
98 __declspec(noinline) void Create(HANDLE file, size_t size); 91 bool Create(HANDLE file, size_t size);
99 void Close(); 92 void Close();
100 93
101 // Returns true iff a mapping has been created. 94 // Returns true iff a mapping has been created.
102 bool valid() const; 95 bool valid() const;
103 96
104 // Returns a writable pointer to the beginning of the memory mapped file. 97 // Returns a writable pointer to the beginning of the memory mapped file.
105 // If Create has not been called successfully, return value is NULL. 98 // If Create has not been called successfully, return value is NULL.
106 void* view() const; 99 void* view() const;
107 100
108 protected: 101 protected:
109 __declspec(noinline) void InitializeView(size_t size); 102 bool InitializeView(size_t size);
110 103
111 HANDLE mapping_; 104 HANDLE mapping_;
112 void* view_; 105 void* view_;
113 }; 106 };
114 107
115 // Manages a temporary file and a memory mapping of the temporary file. 108 // Manages a temporary file and a memory mapping of the temporary file.
116 // The memory that this class manages holds a pointer back to the TempMapping 109 // The memory that this class manages holds a pointer back to the TempMapping
117 // object itself, so that given a memory pointer allocated by this class, 110 // object itself, so that given a memory pointer allocated by this class,
118 // you can get a pointer to the TempMapping instance that owns that memory. 111 // you can get a pointer to the TempMapping instance that owns that memory.
119 class TempMapping { 112 class TempMapping {
120 public: 113 public:
121 TempMapping(); 114 TempMapping();
122 ~TempMapping(); 115 ~TempMapping();
123 116
124 // Creates a temporary file of size |size| and maps it into the current 117 // Creates a temporary file of size |size| and maps it into the current
125 // process' address space. 118 // process's address space.
126 __declspec(noinline) void Initialize(size_t size); 119 bool Initialize(size_t size);
127 120
128 // Returns a writable pointer to the reserved memory. 121 // Returns a writable pointer to the reserved memory.
129 void* memory() const; 122 void* memory() const;
130 123
124 // Returns true if the mapping is valid and memory is available.
125 bool valid() const;
126
131 // Returns a pointer to the TempMapping instance that allocated the |mem| 127 // Returns a pointer to the TempMapping instance that allocated the |mem|
132 // block of memory. It's the callers responsibility to make sure that 128 // block of memory. It's the callers responsibility to make sure that
133 // the memory block was allocated by the TempMapping class. 129 // the memory block was allocated by the TempMapping class.
134 static TempMapping* GetMappingFromPtr(void* mem); 130 static TempMapping* GetMappingFromPtr(void* mem);
135 131
136 protected: 132 protected:
137 TempFile file_; 133 TempFile file_;
138 FileMapping mapping_; 134 FileMapping mapping_;
139 }; 135 };
140 136
141 // An STL compatible memory allocator class that allocates memory either 137 // A memory allocator class that allocates memory either from the heap or via a
142 // from the heap or via a temporary file. A file allocation will be made 138 // temporary file. The interface is STL inspired but the class does not throw
143 // if either the requested memory size exceeds |kMaxHeapAllocationSize| 139 // STL exceptions on allocation failure. Instead it returns NULL.
144 // or if a heap allocation fails. 140 // A file allocation will be made if either the requested memory size exceeds
141 // |kMaxHeapAllocationSize| or if a heap allocation fails.
145 // Allocating the memory as a mapping of a temporary file solves the problem 142 // Allocating the memory as a mapping of a temporary file solves the problem
146 // that there might not be enough physical memory and pagefile to support the 143 // that there might not be enough physical memory and pagefile to support the
147 // allocation. This can happen because these resources are too small, or 144 // allocation. This can happen because these resources are too small, or
148 // already committed to other processes. Provided there is enough disk, the 145 // already committed to other processes. Provided there is enough disk, the
149 // temporary file acts like a pagefile that other processes can't access. 146 // temporary file acts like a pagefile that other processes can't access.
150 template<class T> 147 template<class T>
151 class MemoryAllocator { 148 class MemoryAllocator {
152 public: 149 public:
153 typedef T value_type; 150 typedef T value_type;
154 typedef value_type* pointer; 151 typedef value_type* pointer;
(...skipping 12 matching lines...) Expand all
167 164
168 // 5MB is the maximum heap allocation size that we'll attempt. 165 // 5MB is the maximum heap allocation size that we'll attempt.
169 // When applying a patch for Chrome 10.X we found that at this 166 // When applying a patch for Chrome 10.X we found that at this
170 // threshold there were 17 allocations higher than this threshold 167 // threshold there were 17 allocations higher than this threshold
171 // (largest at 136MB) 10 allocations just below the threshold and 6362 168 // (largest at 136MB) 10 allocations just below the threshold and 6362
172 // smaller allocations. 169 // smaller allocations.
173 static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5; 170 static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5;
174 171
175 template<class OtherT> 172 template<class OtherT>
176 struct rebind { 173 struct rebind {
177 // convert an MemoryAllocator<T> to a MemoryAllocator<OtherT> 174 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
178 typedef MemoryAllocator<OtherT> other; 175 typedef MemoryAllocator<OtherT> other;
179 }; 176 };
180 177
181 MemoryAllocator() _THROW0() { 178 MemoryAllocator() _THROW0() {
182 } 179 }
183 180
184 // We can't use an explicit constructor here, as dictated by our style guide. 181 // We can't use an explicit constructor here, as dictated by our style guide.
185 // The implementation of basic_string in Visual Studio 2010 prevents this. 182 // The implementation of basic_string in Visual Studio 2010 prevents this.
186 MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() { 183 MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() { // NOLINT
187 } 184 }
188 185
189 template<class OtherT> 186 template<class OtherT>
190 explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() { 187 MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() { // NOLINT
191 } 188 }
192 189
193 ~MemoryAllocator() { 190 ~MemoryAllocator() {
194 } 191 }
195 192
196 void deallocate(pointer ptr, size_type size) { 193 void deallocate(pointer ptr, size_type size) {
197 uint8* mem = reinterpret_cast<uint8*>(ptr); 194 uint8* mem = reinterpret_cast<uint8*>(ptr);
198 mem -= sizeof(T); 195 mem -= sizeof(T);
199 if (mem[0] == HEAP_ALLOCATION) { 196 if (mem[0] == HEAP_ALLOCATION) {
200 delete [] mem; 197 delete [] mem;
201 } else { 198 } else {
202 DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]); 199 DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]);
203 TempMapping* mapping = TempMapping::GetMappingFromPtr(mem); 200 TempMapping* mapping = TempMapping::GetMappingFromPtr(mem);
204 delete mapping; 201 delete mapping;
205 } 202 }
206 } 203 }
207 204
208 pointer allocate(size_type count) { 205 pointer allocate(size_type count) {
209 // We use the first byte of each allocation to mark the allocation type. 206 // We use the first byte of each allocation to mark the allocation type.
210 // However, so that the allocation is properly aligned, we allocate an 207 // However, so that the allocation is properly aligned, we allocate an
211 // extra element and then use the first byte of the first element 208 // extra element and then use the first byte of the first element
212 // to mark the allocation type. 209 // to mark the allocation type.
213 count++; 210 count++;
214 211
215 if (count > max_size()) 212 if (count > max_size())
216 throw std::length_error("overflow"); 213 return NULL;
217 214
218 size_type bytes = count * sizeof(T); 215 size_type bytes = count * sizeof(T);
219 uint8* mem = NULL; 216 uint8* mem = NULL;
220 217
221 // First see if we can do this allocation on the heap. 218 // First see if we can do this allocation on the heap.
222 if (count < kMaxHeapAllocationSize) 219 if (count < kMaxHeapAllocationSize)
223 mem = new(std::nothrow) uint8[bytes]; 220 mem = new(std::nothrow) uint8[bytes];
224 if (mem != NULL) { 221 if (mem != NULL) {
225 mem[0] = static_cast<uint8>(HEAP_ALLOCATION); 222 mem[0] = static_cast<uint8>(HEAP_ALLOCATION);
226 } else { 223 } else {
227 // If either the heap allocation failed or the request exceeds the 224 // If either the heap allocation failed or the request exceeds the
228 // max heap allocation threshold, we back the allocation with a temp file. 225 // max heap allocation threshold, we back the allocation with a temp file.
229 TempMapping* mapping = new TempMapping(); 226 TempMapping* mapping = new(std::nothrow) TempMapping();
230 mapping->Initialize(bytes); 227 if (mapping && mapping->Initialize(bytes)) {
231 mem = reinterpret_cast<uint8*>(mapping->memory()); 228 mem = reinterpret_cast<uint8*>(mapping->memory());
232 mem[0] = static_cast<uint8>(FILE_ALLOCATION); 229 mem[0] = static_cast<uint8>(FILE_ALLOCATION);
230 }
233 } 231 }
234 return reinterpret_cast<pointer>(mem + sizeof(T)); 232 return mem ? reinterpret_cast<pointer>(mem + sizeof(T)) : NULL;
235 } 233 }
236 234
237 pointer allocate(size_type count, const void* hint) { 235 pointer allocate(size_type count, const void* hint) {
238 return allocate(count); 236 return allocate(count);
239 } 237 }
240 238
241 void construct(pointer ptr, const T& value) { 239 void construct(pointer ptr, const T& value) {
242 ::new(ptr) T(value); 240 ::new(ptr) T(value);
243 } 241 }
244 242
245 void destroy(pointer ptr) { 243 void destroy(pointer ptr) {
246 ptr->~T(); 244 ptr->~T();
247 } 245 }
248 246
249 size_t max_size() const _THROW0() { 247 size_type max_size() const _THROW0() {
250 size_type count = static_cast<size_type>(-1) / sizeof(T); 248 size_type count = static_cast<size_type>(-1) / sizeof(T);
251 return (0 < count ? count : 1); 249 return (0 < count ? count : 1);
252 } 250 }
253 }; 251 };
254 252
255 #else // OS_WIN 253 #else // OS_WIN
256 254
257 // On Mac, Linux, we just use the default STL allocator. 255 // On Mac, Linux, we use a bare bones implementation that only does
256 // heap allocations.
258 template<class T> 257 template<class T>
259 class MemoryAllocator : public std::allocator<T> { 258 class MemoryAllocator {
260 public: 259 public:
260 typedef T value_type;
261 typedef value_type* pointer;
262 typedef value_type& reference;
263 typedef const value_type* const_pointer;
264 typedef const value_type& const_reference;
265 typedef size_t size_type;
266 typedef ptrdiff_t difference_type;
267
268 template<class OtherT>
269 struct rebind {
270 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
271 typedef MemoryAllocator<OtherT> other;
272 };
273
274 MemoryAllocator() {
275 }
276
277 explicit MemoryAllocator(const MemoryAllocator<T>& other) {
278 }
279
280 template<class OtherT>
281 explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) {
282 }
283
284 ~MemoryAllocator() {
285 }
286
287 void deallocate(pointer ptr, size_type size) {
288 delete [] ptr;
289 }
290
291 pointer allocate(size_type count) {
292 if (count > max_size())
293 return NULL;
294 return reinterpret_cast<pointer>(
295 new(std::nothrow) uint8[count * sizeof(T)]);
296 }
297
298 pointer allocate(size_type count, const void* hint) {
299 return allocate(count);
300 }
301
302 void construct(pointer ptr, const T& value) {
303 ::new(ptr) T(value);
304 }
305
306 void destroy(pointer ptr) {
307 ptr->~T();
308 }
309
310 size_type max_size() const {
311 size_type count = static_cast<size_type>(-1) / sizeof(T);
312 return (0 < count ? count : 1);
313 }
261 }; 314 };
262 315
263 #endif // OS_WIN 316 #endif // OS_WIN
264 317
318 // Manages a growable buffer. The buffer allocation is done by the
319 // MemoryAllocator class. This class will not throw exceptions so call sites
320 // must be prepared to handle memory allocation failures.
321 // The interface is STL inspired to avoid having to make too many changes
322 // to code that previously was using STL.
323 template<typename T, class Allocator = MemoryAllocator<T> >
324 class NoThrowBuffer {
325 public:
326 typedef T value_type;
327 static const size_t kAllocationFailure = 0xffffffff;
328 static const size_t kStartSize = sizeof(T) > 0x100 ? 1 : 0x100 / sizeof(T);
329
330 NoThrowBuffer() : buffer_(NULL), size_(0), alloc_size_(0) {
331 }
332
333 ~NoThrowBuffer() {
334 clear();
335 }
336
337 void clear() {
338 if (buffer_) {
339 alloc_.deallocate(buffer_, alloc_size_);
340 buffer_ = NULL;
341 size_ = 0;
342 alloc_size_ = 0;
343 }
344 }
345
346 bool empty() const {
347 return size_ == 0;
348 }
349
350 CheckBool reserve(size_t size) WARN_UNUSED_RESULT {
351 if (failed())
352 return false;
353
354 if (size <= alloc_size_)
355 return true;
356
357 if (size < kStartSize)
358 size = kStartSize;
359
360 T* new_buffer = alloc_.allocate(size);
361 if (!new_buffer) {
362 clear();
363 alloc_size_ = kAllocationFailure;
364 } else {
365 if (buffer_) {
366 memcpy(new_buffer, buffer_, size_ * sizeof(T));
367 alloc_.deallocate(buffer_, alloc_size_);
368 }
369 buffer_ = new_buffer;
370 alloc_size_ = size;
371 }
372
373 return !failed();
374 }
375
376 CheckBool append(const T* data, size_t size) WARN_UNUSED_RESULT {
377 if (failed())
378 return false;
379
380 if (size > alloc_.max_size() - size_)
381 return false;
382
383 if (!size)
384 return true;
385
386 if ((alloc_size_ - size_) < size) {
387 const size_t max_size = alloc_.max_size();
388 size_t new_size = alloc_size_ ? alloc_size_ : kStartSize;
389 while (new_size < size_ + size) {
390 if (new_size < max_size - new_size) {
391 new_size *= 2;
392 } else {
393 new_size = max_size;
394 }
395 }
396 if (!reserve(new_size))
397 return false;
398 }
399
400 memcpy(buffer_ + size_, data, size * sizeof(T));
401 size_ += size;
402
403 return true;
404 }
405
406 CheckBool resize(size_t size, const T& init_value) WARN_UNUSED_RESULT {
407 if (size > size_) {
408 if (!reserve(size))
409 return false;
410 for (size_t i = size_; i < size; ++i)
411 buffer_[i] = init_value;
412 } else if (size < size_) {
413 // TODO(tommi): Should we allocate a new, smaller buffer?
414 // It might be faster for us to simply change the size.
415 }
416
417 size_ = size;
418
419 return true;
420 }
421
422 CheckBool push_back(const T& item) WARN_UNUSED_RESULT {
423 return append(&item, 1);
424 }
425
426 const T& back() const {
427 return buffer_[size_ - 1];
428 }
429
430 T& back() {
431 return buffer_[size_ - 1];
432 }
433
434 const T* begin() const {
435 if (!size_)
436 return NULL;
437 return &buffer_[0];
438 }
439
440 T* begin() {
441 if (!size_)
442 return NULL;
443 return &buffer_[0];
444 }
445
446 const T* end() const {
447 if (!size_)
448 return NULL;
449 return &buffer_[size_ - 1];
450 }
451
452 T* end() {
453 if (!size_)
454 return NULL;
455 return &buffer_[size_ - 1];
456 }
457
458 const T& operator[](size_t index) const {
459 DCHECK(index < size_);
460 return buffer_[index];
461 }
462
463 T& operator[](size_t index) {
464 DCHECK(index < size_);
465 return buffer_[index];
466 }
467
468 size_t size() const {
469 return size_;
470 }
471
472 T* data() const {
473 return buffer_;
474 }
475
476 // Returns true if an allocation failure has ever occurred for this object.
477 bool failed() const {
478 return alloc_size_ == kAllocationFailure;
479 }
480
481 protected:
482 T* buffer_;
483 size_t size_; // how much of the buffer we're using.
484 size_t alloc_size_; // how much space we have allocated.
485 Allocator alloc_;
486 };
487
265 } // namespace courgette 488 } // namespace courgette
266 489
267 #endif // COURGETTE_MEMORY_ALLOCATOR_H_ 490 #endif // COURGETTE_MEMORY_ALLOCATOR_H_
OLDNEW
« no previous file with comments | « courgette/ensemble_create.cc ('k') | courgette/memory_allocator.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698