美文网首页
webrtc 内存缓存池

webrtc 内存缓存池

作者: gykimo | 来源:发表于2021-05-08 10:31 被阅读0次

用途

内存缓存池,主要是解决频繁malloc和free内存时,会造成很大的性能开销;而且长时间运行后,会造成很多的内存碎片,运行时间越长,malloc和free的时间越长。
缓存池就是提前分配一些内存,使用的时候,直接从缓存池中获取,这样可以节省malloc和free的时间,而且很多时候(甚至大部分时候)free的耗时会高于malloc。

代码分析

webrtc的内存缓存池实现得还是很简单的一种。
它主要解决图像的缓存问题,而且为了简单,一个pool仅支持相同大小的图像。
我们主要看CreateI420Buffer的实现,先判断下是否有空闲的内存使用(通过引用计数),如果有直接返回空闲的,如果没有就创建一个,然后将其插入到内存list缓存中,用于之后使用。

其他注意点

  1. 可以控制缓存池的最大数量,max_number_of_buffers_,如果缓存池满了,那么CreateI420Buffer可能会返回null。就如malloc可能返回null一样。

源码

https://webrtc.googlesource.com/src/+/refs/heads/master/common_video/video_frame_buffer_pool.cc

#include "common_video/include/video_frame_buffer_pool.h"

#include <limits>

#include "rtc_base/checks.h"

namespace webrtc {

namespace {
bool HasOneRef(const rtc::scoped_refptr<VideoFrameBuffer>& buffer) {
  // Cast to rtc::RefCountedObject is safe because this function is only called
  // on locally created VideoFrameBuffers, which are either
  // |rtc::RefCountedObject<I420Buffer>| or |rtc::RefCountedObject<NV12Buffer>|.
  switch (buffer->type()) {
    case VideoFrameBuffer::Type::kI420: {
      return static_cast<rtc::RefCountedObject<I420Buffer>*>(buffer.get())
          ->HasOneRef();
    }
    case VideoFrameBuffer::Type::kNV12: {
      return static_cast<rtc::RefCountedObject<NV12Buffer>*>(buffer.get())
          ->HasOneRef();
    }
    default:
      RTC_NOTREACHED();
  }
  return false;
}

}  // namespace

VideoFrameBufferPool::VideoFrameBufferPool() : VideoFrameBufferPool(false) {}

VideoFrameBufferPool::VideoFrameBufferPool(bool zero_initialize)
    : VideoFrameBufferPool(zero_initialize,
                           std::numeric_limits<size_t>::max()) {}

VideoFrameBufferPool::VideoFrameBufferPool(bool zero_initialize,
                                           size_t max_number_of_buffers)
    : zero_initialize_(zero_initialize),
      max_number_of_buffers_(max_number_of_buffers) {}

VideoFrameBufferPool::~VideoFrameBufferPool() = default;

void VideoFrameBufferPool::Release() {
  buffers_.clear();
}

bool VideoFrameBufferPool::Resize(size_t max_number_of_buffers) {
  RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
  size_t used_buffers_count = 0;
  for (const rtc::scoped_refptr<VideoFrameBuffer>& buffer : buffers_) {
    // If the buffer is in use, the ref count will be >= 2, one from the list we
    // are looping over and one from the application. If the ref count is 1,
    // then the list we are looping over holds the only reference and it's safe
    // to reuse.
    if (!HasOneRef(buffer)) {
      used_buffers_count++;
    }
  }
  if (used_buffers_count > max_number_of_buffers) {
    return false;
  }
  max_number_of_buffers_ = max_number_of_buffers;

  size_t buffers_to_purge = buffers_.size() - max_number_of_buffers_;
  auto iter = buffers_.begin();
  while (iter != buffers_.end() && buffers_to_purge > 0) {
    if (HasOneRef(*iter)) {
      iter = buffers_.erase(iter);
      buffers_to_purge--;
    } else {
      ++iter;
    }
  }
  return true;
}

rtc::scoped_refptr<I420Buffer> VideoFrameBufferPool::CreateI420Buffer(
    int width,
    int height) {
  RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);

  rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
      GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI420);
  if (existing_buffer) {
    // Cast is safe because the only way kI420 buffer is created is
    // in the same function below, where |RefCountedObject<I420Buffer>| is
    // created.
    rtc::RefCountedObject<I420Buffer>* raw_buffer =
        static_cast<rtc::RefCountedObject<I420Buffer>*>(existing_buffer.get());
    // Creates a new scoped_refptr, which is also pointing to the same
    // RefCountedObject as buffer, increasing ref count.
    return rtc::scoped_refptr<I420Buffer>(raw_buffer);
  }

  if (buffers_.size() >= max_number_of_buffers_)
    return nullptr;
  // Allocate new buffer.
  rtc::scoped_refptr<I420Buffer> buffer =
      rtc::make_ref_counted<I420Buffer>(width, height);

  if (zero_initialize_)
    buffer->InitializeData();

  buffers_.push_back(buffer);
  return buffer;
}

rtc::scoped_refptr<NV12Buffer> VideoFrameBufferPool::CreateNV12Buffer(
    int width,
    int height) {
  RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);

  rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
      GetExistingBuffer(width, height, VideoFrameBuffer::Type::kNV12);
  if (existing_buffer) {
    // Cast is safe because the only way kI420 buffer is created is
    // in the same function below, where |RefCountedObject<I420Buffer>| is
    // created.
    rtc::RefCountedObject<NV12Buffer>* raw_buffer =
        static_cast<rtc::RefCountedObject<NV12Buffer>*>(existing_buffer.get());
    // Creates a new scoped_refptr, which is also pointing to the same
    // RefCountedObject as buffer, increasing ref count.
    return rtc::scoped_refptr<NV12Buffer>(raw_buffer);
  }

  if (buffers_.size() >= max_number_of_buffers_)
    return nullptr;
  // Allocate new buffer.
  rtc::scoped_refptr<NV12Buffer> buffer =
      rtc::make_ref_counted<NV12Buffer>(width, height);

  if (zero_initialize_)
    buffer->InitializeData();

  buffers_.push_back(buffer);
  return buffer;
}

rtc::scoped_refptr<VideoFrameBuffer> VideoFrameBufferPool::GetExistingBuffer(
    int width,
    int height,
    VideoFrameBuffer::Type type) {
  // Release buffers with wrong resolution or different type.
  for (auto it = buffers_.begin(); it != buffers_.end();) {
    const auto& buffer = *it;
    if (buffer->width() != width || buffer->height() != height ||
        buffer->type() != type) {
      it = buffers_.erase(it);
    } else {
      ++it;
    }
  }
  // Look for a free buffer.
  for (const rtc::scoped_refptr<VideoFrameBuffer>& buffer : buffers_) {
    // If the buffer is in use, the ref count will be >= 2, one from the list we
    // are looping over and one from the application. If the ref count is 1,
    // then the list we are looping over holds the only reference and it's safe
    // to reuse.
    if (HasOneRef(buffer)) {
      RTC_CHECK(buffer->type() == type);
      return buffer;
    }
  }
  return nullptr;
}

}  // namespace webrtc

相关文章

  • webrtc 内存缓存池

    用途 内存缓存池,主要是解决频繁malloc和free内存时,会造成很大的性能开销;而且长时间运行后,会造成很多的...

  • 每日记录之MysqlInnoDB缓存

    每日 InnoDB缓存池 1. 缓存池 mysql专用服务器的80%的物理内存都会被用来缓存数据 为了保证大批量读...

  • JDK源码| Integer的IntegerCache(缓存池)

    Integer缓存池是jdk5中引入的一个有助于节省内存,提高性能的新特性 Integer缓存池范围 -128~1...

  • Bitmap缓存-内存缓存+磁盘缓存+复用池

    这一篇是在Glide开发之旅1的文章上面的扩展,这里讲到了二级缓存(内存+磁盘)再结合复用池的理念去写的一款简单易...

  • 内核内存分配

    1、kmalloc 2、slab高速缓存内存池 3、vmalloc 4、大缓冲区

  • iOS有关于性能优化的建议

    1.使用RAC管理内存 2.UItableViewCell和UICollectionViewCell的缓存池的使用...

  • YYCache 源码学习总结

    YYCache 内存分为内存缓存和磁盘缓存 内存缓存 1.内存缓存实现通过CFMutableDictionaryR...

  • InnoDB 缓冲池

    InnoDB 缓冲池 InnoDB 维护一个称为缓冲池的存储区域, 用于缓存内存中的数据和索引缓冲池的大小当然是越...

  • 区分SDWebImage的三种缓存

    SDWebImage的三种缓存分为:内存图片缓存、磁盘图片缓存、内存操作缓存步骤如下1、先查看内存图片缓存,内存图...

  • mysql 参数调优(1)之内存缓冲池innodb_buffer

    内存的大小是最能直接反映数据库的性能。InnoDB存储引擎既缓存数据,又缓存索引,并且将它们缓存于一个很大的缓冲池...

网友评论

      本文标题:webrtc 内存缓存池

      本文链接:https://www.haomeiwen.com/subject/pbyedltx.html