LCOV - code coverage report
Current view: top level - src/zone - accounting-allocator.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 83 85 97.6 %
Date: 2017-10-20 Functions: 14 15 93.3 %

          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(&current_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(&current_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(&current_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(&current_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 :           &current_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(&current_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

Generated by: LCOV version 1.10