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

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: '' 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 __declspec(noinline)
grt (UTC plus 2) 2011/04/06 02:46:38 Should this __declspec go away, too?
tommi (sloooow) - chröme 2011/04/06 13:50:02 Done.
103 bool InitializeView(size_t size);
110 104
111 HANDLE mapping_; 105 HANDLE mapping_;
112 void* view_; 106 void* view_;
113 }; 107 };
114 108
115 // Manages a temporary file and a memory mapping of the temporary file. 109 // 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 110 // 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, 111 // 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. 112 // you can get a pointer to the TempMapping instance that owns that memory.
119 class TempMapping { 113 class TempMapping {
120 public: 114 public:
121 TempMapping(); 115 TempMapping();
122 ~TempMapping(); 116 ~TempMapping();
123 117
124 // Creates a temporary file of size |size| and maps it into the current 118 // Creates a temporary file of size |size| and maps it into the current
125 // process' address space. 119 // process' address space.
grt (UTC plus 2) 2011/04/06 02:46:38 process's
tommi (sloooow) - chröme 2011/04/06 13:50:02 Done.
126 __declspec(noinline) void Initialize(size_t size); 120 bool Initialize(size_t size);
127 121
128 // Returns a writable pointer to the reserved memory. 122 // Returns a writable pointer to the reserved memory.
129 void* memory() const; 123 void* memory() const;
130 124
125 // Returns true if the mapping is valid and memory is available.
126 bool valid() const;
127
131 // Returns a pointer to the TempMapping instance that allocated the |mem| 128 // Returns a pointer to the TempMapping instance that allocated the |mem|
132 // block of memory. It's the callers responsibility to make sure that 129 // block of memory. It's the callers responsibility to make sure that
133 // the memory block was allocated by the TempMapping class. 130 // the memory block was allocated by the TempMapping class.
134 static TempMapping* GetMappingFromPtr(void* mem); 131 static TempMapping* GetMappingFromPtr(void* mem);
135 132
136 protected: 133 protected:
137 TempFile file_; 134 TempFile file_;
138 FileMapping mapping_; 135 FileMapping mapping_;
139 }; 136 };
140 137
141 // An STL compatible memory allocator class that allocates memory either 138 // 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 139 // temporary file. The interface is STL inspired but the class does not throw
143 // if either the requested memory size exceeds |kMaxHeapAllocationSize| 140 // STL exceptions on allocation failure. Instead it returns NULL.
144 // or if a heap allocation fails. 141 // implementation that throws those exceptions.
grt (UTC plus 2) 2011/04/06 02:46:38 should this line be removed?
tommi (sloooow) - chröme 2011/04/06 13:50:02 thanks!
142 // A file allocation will be made if either the requested memory size exceeds
143 // |kMaxHeapAllocationSize| or if a heap allocation fails.
145 // Allocating the memory as a mapping of a temporary file solves the problem 144 // 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 145 // 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 146 // allocation. This can happen because these resources are too small, or
148 // already committed to other processes. Provided there is enough disk, the 147 // already committed to other processes. Provided there is enough disk, the
149 // temporary file acts like a pagefile that other processes can't access. 148 // temporary file acts like a pagefile that other processes can't access.
150 template<class T> 149 template<class T>
151 class MemoryAllocator { 150 class MemoryAllocator {
152 public: 151 public:
153 typedef T value_type; 152 typedef T value_type;
154 typedef value_type* pointer; 153 typedef value_type* pointer;
(...skipping 12 matching lines...) Expand all
167 166
168 // 5MB is the maximum heap allocation size that we'll attempt. 167 // 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 168 // When applying a patch for Chrome 10.X we found that at this
170 // threshold there were 17 allocations higher than this threshold 169 // threshold there were 17 allocations higher than this threshold
171 // (largest at 136MB) 10 allocations just below the threshold and 6362 170 // (largest at 136MB) 10 allocations just below the threshold and 6362
172 // smaller allocations. 171 // smaller allocations.
173 static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5; 172 static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5;
174 173
175 template<class OtherT> 174 template<class OtherT>
176 struct rebind { 175 struct rebind {
177 // convert an MemoryAllocator<T> to a MemoryAllocator<OtherT> 176 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
178 typedef MemoryAllocator<OtherT> other; 177 typedef MemoryAllocator<OtherT> other;
179 }; 178 };
180 179
181 MemoryAllocator() _THROW0() { 180 MemoryAllocator() _THROW0() {
182 } 181 }
183 182
184 // We can't use an explicit constructor here, as dictated by our style guide. 183 // 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. 184 // The implementation of basic_string in Visual Studio 2010 prevents this.
186 MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() { 185 MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() { // NOLINT
187 } 186 }
188 187
189 template<class OtherT> 188 template<class OtherT>
190 explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() { 189 MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() { // NOLINT
191 } 190 }
192 191
193 ~MemoryAllocator() { 192 ~MemoryAllocator() {
194 } 193 }
195 194
196 void deallocate(pointer ptr, size_type size) { 195 void deallocate(pointer ptr, size_type size) {
197 uint8* mem = reinterpret_cast<uint8*>(ptr); 196 uint8* mem = reinterpret_cast<uint8*>(ptr);
198 mem -= sizeof(T); 197 mem -= sizeof(T);
199 if (mem[0] == HEAP_ALLOCATION) { 198 if (mem[0] == HEAP_ALLOCATION) {
200 delete [] mem; 199 delete [] mem;
201 } else { 200 } else {
202 DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]); 201 DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]);
203 TempMapping* mapping = TempMapping::GetMappingFromPtr(mem); 202 TempMapping* mapping = TempMapping::GetMappingFromPtr(mem);
204 delete mapping; 203 delete mapping;
205 } 204 }
206 } 205 }
207 206
208 pointer allocate(size_type count) { 207 pointer allocate(size_type count) {
209 // We use the first byte of each allocation to mark the allocation type. 208 // 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 209 // However, so that the allocation is properly aligned, we allocate an
211 // extra element and then use the first byte of the first element 210 // extra element and then use the first byte of the first element
212 // to mark the allocation type. 211 // to mark the allocation type.
213 count++; 212 count++;
214 213
215 if (count > max_size()) 214 if (count > max_size())
216 throw std::length_error("overflow"); 215 return NULL;
217 216
218 size_type bytes = count * sizeof(T); 217 size_type bytes = count * sizeof(T);
219 uint8* mem = NULL; 218 uint8* mem = NULL;
220 219
221 // First see if we can do this allocation on the heap. 220 // First see if we can do this allocation on the heap.
222 if (count < kMaxHeapAllocationSize) 221 if (count < kMaxHeapAllocationSize)
223 mem = new(std::nothrow) uint8[bytes]; 222 mem = new(std::nothrow) uint8[bytes];
224 if (mem != NULL) { 223 if (mem != NULL) {
225 mem[0] = static_cast<uint8>(HEAP_ALLOCATION); 224 mem[0] = static_cast<uint8>(HEAP_ALLOCATION);
226 } else { 225 } else {
227 // If either the heap allocation failed or the request exceeds the 226 // If either the heap allocation failed or the request exceeds the
228 // max heap allocation threshold, we back the allocation with a temp file. 227 // max heap allocation threshold, we back the allocation with a temp file.
229 TempMapping* mapping = new TempMapping(); 228 TempMapping* mapping = new(std::nothrow) TempMapping();
230 mapping->Initialize(bytes); 229 if (mapping && mapping->Initialize(bytes)) {
231 mem = reinterpret_cast<uint8*>(mapping->memory()); 230 mem = reinterpret_cast<uint8*>(mapping->memory());
232 mem[0] = static_cast<uint8>(FILE_ALLOCATION); 231 mem[0] = static_cast<uint8>(FILE_ALLOCATION);
232 }
233 } 233 }
234 return reinterpret_cast<pointer>(mem + sizeof(T)); 234 return mem ? reinterpret_cast<pointer>(mem + sizeof(T)) : NULL;
235 } 235 }
236 236
237 pointer allocate(size_type count, const void* hint) { 237 pointer allocate(size_type count, const void* hint) {
238 return allocate(count); 238 return allocate(count);
239 } 239 }
240 240
241 void construct(pointer ptr, const T& value) { 241 void construct(pointer ptr, const T& value) {
242 ::new(ptr) T(value); 242 ::new(ptr) T(value);
243 } 243 }
244 244
245 void destroy(pointer ptr) { 245 void destroy(pointer ptr) {
246 ptr->~T(); 246 ptr->~T();
247 } 247 }
248 248
249 size_t max_size() const _THROW0() { 249 size_t max_size() const _THROW0() {
250 size_type count = static_cast<size_type>(-1) / sizeof(T); 250 size_type count = static_cast<size_type>(-1) / sizeof(T);
251 return (0 < count ? count : 1); 251 return (0 < count ? count : 1);
252 } 252 }
253 }; 253 };
254 254
255 #else // OS_WIN 255 #else // OS_WIN
256 256
257 // On Mac, Linux, we just use the default STL allocator. 257 // On Mac, Linux, we use a bare bones implementation that only does
258 // heap allocations.
258 template<class T> 259 template<class T>
259 class MemoryAllocator : public std::allocator<T> { 260 class MemoryAllocator {
260 public: 261 public:
262 typedef T value_type;
263 typedef value_type* pointer;
264 typedef value_type& reference;
265 typedef const value_type* const_pointer;
266 typedef const value_type& const_reference;
267 typedef size_t size_type;
268 typedef ptrdiff_t difference_type;
269
270 template<class OtherT>
271 struct rebind {
272 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
273 typedef MemoryAllocator<OtherT> other;
274 };
275
276 MemoryAllocator() {
277 }
278
279 explicit MemoryAllocator(const MemoryAllocator<T>& other) {
280 }
281
282 template<class OtherT>
283 explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) {
284 }
285
286 ~MemoryAllocator() {
287 }
288
289 void deallocate(pointer ptr, size_type size) {
290 delete [] ptr;
291 }
292
293 pointer allocate(size_type count) {
294 if (count > max_size())
295 return NULL;
296 return reinterpret_cast<pointer>(
297 new(std::nothrow) uint8[count * sizeof(T)]);
298 }
299
300 pointer allocate(size_type count, const void* hint) {
301 return allocate(count);
302 }
303
304 void construct(pointer ptr, const T& value) {
305 ::new(ptr) T(value);
306 }
307
308 void destroy(pointer ptr) {
309 ptr->~T();
310 }
311
312 size_t max_size() const () {
313 size_type count = static_cast<size_type>(-1) / sizeof(T);
314 return (0 < count ? count : 1);
315 }
261 }; 316 };
262 317
263 #endif // OS_WIN 318 #endif // OS_WIN
264 319
320 // Manages a growable buffer. The buffer allocation is done by the
321 // MemoryAllocator class. This class will not throw exceptions so call sites
322 // must be prepared to handle memory allocation failures.
323 // The interface is STL inspired to avoid having to make too many changes
324 // to code that previously was using STL.
325 template<typename T, class Allocator = MemoryAllocator<T> >
326 class NoThrowBuffer {
327 public:
328 typedef T value_type;
329 static const size_t kAllocationFailure = 0xffffffff;
330 static const size_t kStartSize = sizeof(T) > 0x100 ? 1 : 0x100 / sizeof(T);
331
332 NoThrowBuffer() : buffer_(NULL), size_(0), alloc_size_(0) {
333 }
334
335 ~NoThrowBuffer() {
336 clear();
337 }
338
339 void clear() {
340 if (buffer_) {
341 alloc_.deallocate(buffer_, alloc_size_);
342 buffer_ = NULL;
343 size_ = 0;
344 alloc_size_ = 0;
345 }
346 }
347
348 bool empty() const {
349 return size_ == 0;
350 }
351
352 CheckBool reserve(size_t size) WARN_UNUSED_RESULT {
353 if (failed())
354 return false;
355
356 if (size <= alloc_size_)
357 return true;
358
359 if (size < kStartSize)
360 size = kStartSize;
361
362 T* new_buffer = alloc_.allocate(size);
363 if (!new_buffer) {
364 clear();
365 alloc_size_ = kAllocationFailure;
366 } else {
367 if (buffer_) {
368 memcpy(new_buffer, buffer_, size_ * sizeof(T));
369 alloc_.deallocate(buffer_, alloc_size_);
370 }
371 buffer_ = new_buffer;
372 alloc_size_ = size;
373 }
374
375 return !failed();
376 }
377
378 CheckBool append(const T* data, size_t size) WARN_UNUSED_RESULT {
379 if (failed())
380 return false;
381
382 if (size > alloc_.max_size() - size_)
383 return false;
384
385 if (!size)
386 return true;
387
388 if ((alloc_size_ - size_) < size) {
389 const size_t max_size = alloc_.max_size();
390 size_t new_size = alloc_size_ ? alloc_size_ : kStartSize;
391 while (new_size < size_ + size) {
392 if (new_size < max_size - new_size) {
393 new_size *= 2;
394 } else {
395 new_size = max_size;
396 }
397 }
398 if (!reserve(new_size))
399 return false;
400 }
401
402 memcpy(buffer_ + size_, data, size * sizeof(T));
403 size_ += size;
404
405 return true;
406 }
407
408 CheckBool resize(size_t size, const T& init_value) WARN_UNUSED_RESULT {
409 if (size > size_) {
410 if (!reserve(size))
411 return false;
412 for (size_t i = size_; i < size; ++i)
413 buffer_[i] = init_value;
414 } else if (size < size_) {
415 // TODO(tommi): Should we allocate a new, smaller buffer?
416 // It might be faster for us to simply change the size.
417 }
418
419 size_ = size;
420
421 return true;
422 }
423
424 CheckBool push_back(const T& item) WARN_UNUSED_RESULT {
425 return append(&item, 1);
426 }
427
428 const T& back() const {
429 return buffer_[size_ - 1];
430 }
431
432 T& back() {
433 return buffer_[size_ - 1];
434 }
435
436 const T* begin() const {
437 if (!size_)
438 return NULL;
439 return &buffer_[0];
440 }
441
442 T* begin() {
443 if (!size_)
444 return NULL;
445 return &buffer_[0];
446 }
447
448 const T* end() const {
449 if (!size_)
450 return NULL;
451 return &buffer_[size_ - 1];
452 }
453
454 T* end() {
455 if (!size_)
456 return NULL;
457 return &buffer_[size_ - 1];
458 }
459
460 const T& operator[](size_t index) const {
461 DCHECK(index < size_);
462 return buffer_[index];
463 }
464
465 T& operator[](size_t index) {
466 DCHECK(index < size_);
467 return buffer_[index];
468 }
469
470 size_t size() const {
471 return size_;
472 }
473
474 T* data() const {
475 return buffer_;
476 }
477
478 // Returns true if an allocation failure has ever occurred for this object.
479 bool failed() const {
480 return alloc_size_ == kAllocationFailure;
481 }
482
483 protected:
484 T* buffer_;
485 size_t size_; // how much of the buffer we're using.
486 size_t alloc_size_; // how much space we have allocated.
487 Allocator alloc_;
488 };
489
265 } // namespace courgette 490 } // namespace courgette
266 491
267 #endif // COURGETTE_MEMORY_ALLOCATOR_H_ 492 #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