leveldb爬山之路——arena


        1、預備知識——內存池
ide

        內存池是爲了使內存分配的效率獲得提高而採用的一種方法,而且不多產生堆碎片,能夠避免內存泄漏。
函數

        簡單來講,就是每次申請的內存都放入一個容器當中,每次須要申請的內存先看是否能夠從內存池中直接分配,若是不夠,那麼先申請一塊新的內存放入內存池中,而後再進行分配。最後釋放的時候,只須要釋放這個容器管理的內存便可。
源碼分析


        2、源碼分析
性能

        1. arena.h優化

class Arena {
 public:
  Arena();
  ~Arena();

  // Return a pointer to a newly allocated memory block of "bytes" bytes.
  char* Allocate(size_t bytes);

  // Allocate memory with the normal alignment guarantees provided by malloc
  char* AllocateAligned(size_t bytes);

  // Returns an estimate of the total memory usage of data allocated
  // by the arena.
  size_t MemoryUsage() const {
    return reinterpret_cast<uintptr_t>(memory_usage_.NoBarrier_Load());
  }

 private:
  char* AllocateFallback(size_t bytes);            
  char* AllocateNewBlock(size_t block_bytes);      

  // Allocation state
  char* alloc_ptr_;              //內存指針
  size_t alloc_bytes_remaining_;       //內存池中可用內存大小

  // Array of new[] allocated memory blocks
  std::vector<char*> blocks_;            //整個內存池

  // Total memory usage of the arena.
  port::AtomicPointer memory_usage_;        //內存總的使用狀況,即申請了多大的內存

  // No copying allowed
  Arena(const Arena&);
  void operator=(const Arena&);
};


        整個Arena類中,只有三個接口函數能夠調用,他們分別是Allocate,申請內存;AllocateAligned 申請字節對齊的內存;MemoryUsage, 內存使用狀況,三個函數。下面分別對三個函數進行說明ui


        1.1  Allocate函數
spa

inline char* Arena::Allocate(size_t bytes) {
  // The semantics of what to return are a bit messy if we allow
  // 0-byte allocations, so we disallow them here (we don't need
  // them for our internal use).
  assert(bytes > 0);
  if (bytes <= alloc_bytes_remaining_) {
    char* result = alloc_ptr_;     //返回當前內存的地址
    alloc_ptr_ += bytes;            //移動指針,表示內存被佔用
    alloc_bytes_remaining_ -= bytes;    //可用內存數減小
    return result;
  }
  return AllocateFallback(bytes);
}

        若是申請的內存大小不比當前內存池中可用內存的大時,那麼就將直接從當前內存池中得到內存,不然,就須要從新申請內存。
3d

過程以下:指針

image.png

              上圖是初始內存池
orm

image.png


        上圖是bytes<alloc_bytes_remaining_時,申請內存的狀況。

        當bytes>alloc_bytes_remaining_時,這時就調用了AllocateFallback函數。


        1.2    AllocateFallback函數

char* Arena::AllocateFallback(size_t bytes) {
  if (bytes > kBlockSize / 4) {
    // Object is more than a quarter of our block size.  Allocate it separately
    // to avoid wasting too much space in leftover bytes.
    char* result = AllocateNewBlock(bytes);
    return result;
  }

  // We waste the remaining space in the current block.
  alloc_ptr_ = AllocateNewBlock(kBlockSize);
  alloc_bytes_remaining_ = kBlockSize;

  char* result = alloc_ptr_;
  alloc_ptr_ += bytes;
  alloc_bytes_remaining_ -= bytes;
  return result;
}

        從代碼能夠看出,當bytes 比 BlockSize/4, 即大於1024時,那麼就直接從新申請須要大小的內存。可是,若是申請的內存小於1024時,這時候,就是直接申請一塊4096的新內存,而後將可用的內存大小設置爲4096,而後再進行指針的偏移和可用內存的減少。

這裏有兩個特別須要注意的地方:

        1)    大於1024時申請的內存是在以前申請的內存後面,若是下一次申請的內存小於alloc_bytes_remaining_,那麼仍是用當前內存池當中的可用內存。

image.png

        2)    當申請的內存處於大於alloc_bytes_remaining_,而且小於1024時,那麼這個時候就會從新申請一塊4096大小的內存,而且將alloc_bytes_emaining_的大小更新爲4096。這裏意味着浪費了一塊小於1024大小的內存,在查詢資料之後發現,做者浪費就浪費了,只要性能Ok就好了。所以,若是是本身使用的時候,這裏屬於能夠優化的部分。

        image.png

image.png

image.png





        1.3 AllocateNewBlock

char* Arena::AllocateNewBlock(size_t block_bytes) {
  char* result = new char[block_bytes];
  blocks_.push_back(result);
  memory_usage_.NoBarrier_Store(
      reinterpret_cast<void*>(MemoryUsage() + block_bytes + sizeof(char*)));
  return result;
}

        申請一塊新的內存,並將這塊內存放入內存池容器中,而後更新內存使用狀況。


        1.4  AllocateAligned函數

char* Arena::AllocateAligned(size_t bytes) {
  //判斷一個數是否是2的指數次冪的奇淫巧技
  const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8;
  assert((align & (align-1)) == 0);   // Pointer size should be a power of 2
  //判斷內存對齊的奇淫巧技,以及使內存對齊的方法
  size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align-1);
  size_t slop = (current_mod == 0 ? 0 : align - current_mod);
  size_t needed = bytes + slop;
  char* result;
  if (needed <= alloc_bytes_remaining_) {
    result = alloc_ptr_ + slop;
    alloc_ptr_ += needed;
    alloc_bytes_remaining_ -= needed;
  } else {
    // AllocateFallback always returned aligned memory
    result = AllocateFallback(bytes);
  }
  assert((reinterpret_cast<uintptr_t>(result) & (align-1)) == 0);
  return result;
}

        這個函數是用來申請內存對齊的函數。

  


交流QQ羣:199546072

相關文章
相關標籤/搜索