victoriaMetrics之byteBuffer

VictoriaMetrics经常会处理数目庞大的指标,在处理的过程中会涉及指标的拷贝,如果在指标拷贝时都进行内存申请的话,其内存消耗和性能损耗都非常大。victoriaMetrics使用byteBuffer来复用内存,提升性能,其核心就是用了sync.pool。下面主要看下它是如何结合sync.pool运作的。

ByteBuffer的结构体如下,只包含一个切片:

type ByteBuffer struct {
// B is the underlying byte slice.
B []byte
}

ByteBufferPool的用法

为了服用ByteBuffer,victoriaMetrics用到了ByteBufferPool,与常见的sync.Pool用法相同,包含一个Get和一个Put函数。

// ByteBufferPool is a pool of ByteBuffers.
type ByteBufferPool struct {
p sync.Pool
} // Get obtains a ByteBuffer from bbp.
func (bbp *ByteBufferPool) Get() *ByteBuffer {
bbv := bbp.p.Get()
if bbv == nil {
return &ByteBuffer{}
}
return bbv.(*ByteBuffer)
} // Put puts bb into bbp.
func (bbp *ByteBufferPool) Put(bb *ByteBuffer) {
bb.Reset()
bbp.p.Put(bb)
}

Put函数用于将ByteBuffer返回给资源池,为了防止下次使用的时候出现无效数据,在返回给sync.Pool之前需要清空切片内存,其使用的Reset函数如下,bb.B = bb.B[:0]也是一种常见的清空切片内容的方式:

func (bb *ByteBuffer) Reset() {
bb.B = bb.B[:0]
}

ByteBuffer实现了io.Writerio.ReaderFrom接口。

Writer接口实现

实现的write接口如下,比较简单,只是简单地将入参数据添加到byteBuffer中。在append的时候会增加切片的容量。

func (bb *ByteBuffer) Write(p []byte) (int, error) {
bb.B = append(bb.B, p...)
return len(p), nil
}

ReaderFrom接口实现

ReaderFrom中比较有意思的是看它是如何预分配容量,以及在容量不足的情况下,如何进行扩容。其核心思想是使用make预先申请一块内存,而不是通过append来让底层自动扩容。

  1. 首先获取b的长度,表示切片中已有的数据长度

  2. 由于ByteBuffer可能来自ByteBufferPool.Get,因此,其切片容量可能无法满足数据读取的需要,此时用到了ResizeWithCopyMayOverallocateResizeWithCopyMayOverallocate确保切片的容量不小于n字节,如果容量足够,则返回长度为n的子切片,否则申请新的切片,并返回长度为n的子切片。roundToNearestPow2会找出最接近n的2的幂的数值,以此作为新切片的容量。

    // ResizeNoCopyMayOverallocate resizes b to minimum n bytes and returns the resized buffer (which may be newly allocated).
    //
    // If newly allocated buffer is returned then b contents isn't copied to it.
    func ResizeNoCopyMayOverallocate(b []byte, n int) []byte {
    if n <= cap(b) {
    return b[:n]
    }
    nNew := roundToNearestPow2(n)
    bNew := make([]byte, nNew)
    return bNew[:n]
    } // roundToNearestPow2 rounds n to the nearest power of 2
    //
    // It is expected that n > 0
    func roundToNearestPow2(n int) int {
    pow2 := uint8(bits.Len(uint(n - 1)))
    return 1 << pow2
    }
  3. 将b的长度等于容量

  4. 设置offset为b中已有的数据偏移量

  5. 获取剩余的容量free,如果剩余的容量不足一半(free < offset),则将容量翻倍

  6. 将数据读取到offset之后的存储中,并增加偏移量

  7. Read操作返回错误时,将ByteBuffer中的切片长度设置为b,如果返回错误为EOF,则视为数据读取完成。

// ReadFrom reads all the data from r to bb until EOF.
func (bb *ByteBuffer) ReadFrom(r io.Reader) (int64, error) {
b := bb.B
bLen := len(b)//1
b = ResizeWithCopyMayOverallocate(b, 4*1024) //2
b = b[:cap(b)]//3
offset := bLen//4
for {
if free := len(b) - offset; free < offset {//5
n := len(b)
b = append(b, make([]byte, n)...)
}
n, err := r.Read(b[offset:])//6
offset += n
if err != nil {//7
bb.B = b[:offset]
if err == io.EOF {
err = nil
}
return int64(offset - bLen), err//9
}
}
}

总结

后续可以使用该库来满足从io.Reader中读取数据,而不用担心buffer不足,借助ByteBufferPool可以有效地复用buffer。

victoriaMetrics之byteBuffer的更多相关文章

  1. Java--Stream,NIO ByteBuffer,NIO MappedByteBuffer性能对比

    目前Java中最IO有多种文件读取的方法,本文章对比Stream,NIO ByteBuffer,NIO MappedByteBuffer的性能,让我们知道到底怎么能写出性能高的文件读取代码. pack ...

  2. 读取 java.nio.ByteBuffer 中的字符串(String) 写入方式flash.utils.ByteArray.writeUTF

    通过研究ByteArray的写入格式以及方法说明,可以发现writeUTF是先使用2位写入字符串的长度,然后在其后写入字符串编码. flash.utils.ByteArray.writeUTF(val ...

  3. 堆外内存操作类ByteBuffer

    本篇主要讲解如何使用直接内存(堆外内存),并按照下面的步骤进行说明: 1 相关背景-->读写操作-->关键属性-->读写实践-->扩展-->参考说明 希望对想使用直接内存 ...

  4. ByteBuffer

    1.堆内:HeapByteBuffer,在java的堆内创建. 缺点:可能引起堆的不断gc 写文件的时候需要先将堆的buffer写进直接buffer里,然后再写入文件 2.堆外:DirectByteB ...

  5. ByteBuffer解析

    一.前言 前一篇文章我们介绍了Android中直播视频技术的基础大纲知识,这里就开始一一讲解各个知识点,首先主要来看一下视频直播中的一个重要的基础核心类:ByteBuffer,这个类看上去都知道了,是 ...

  6. C#实现ByteBuffer类 .

    在写网络程序的时候,经常需要往一个数组里面压数据或者取数据,而Java中再Java.nio中有个ByteBuffer能很方便的实现,Delphi中也有个Stream类有着同样的功能,这里我就模仿JAV ...

  7. ByteBuffer用法小结

    在NIO中,数据的读写操作始终是与缓冲区相关联的.读取时信道(SocketChannel)将数据读入缓冲区,写入时首先要将发送的数据按顺序填入缓冲区.缓冲区是定长的,基本上它只是一个列表,它的所有元素 ...

  8. 【MINA】缓存区ByteBuffer和IOBuffer你要了解的常用知识

    mina中IOBuffer是Nio中ByteBuffer的衍生类,主要是解决Bytebuffer的两个不足 1.没有提供足够灵活的get/putXXX方法 2.它容量固定,难以写入可变长度的数据 特点 ...

  9. ByteBuffer的allocate和allocateDirect区别

    ByteBuffer的allocate和allocateDirect区别 在Java中当我们要对数据进行更底层的操作时,通常是操作数据的字节(byte)形式,这时常常会用到ByteBuffer这样一个 ...

随机推荐

  1. pandas常用操作详解——pandas的去重操作df.duplicated()与df.drop_duplicates()

    df.duplicated() 参数详解: subset:检测重复的数据范围.默认为数据集的所有列,可指定特定数据列: keep: 标记哪个重复数据,默认为'first'.1.'first':标记重复 ...

  2. MongoDB 事务机制

    MongoDB 从4.0 版本开始 副本集支持多文档事务,4.2 版本开始分片集群也支持多文档事务.单个集合的单个文档事务 在 1.x 就支持. 以下是跟 mongo 事务相关的一些概念: 1. Wr ...

  3. 学习廖雪峰的Git教程2--远程仓库

    今天跳过之前版本管理,先来学习远程仓库内容: 1.创建ssh(这是为没有ssh key准备的,如果有就可以进行下一步: 敲入 $ ssh-keygen -t rsa -C "youremai ...

  4. [源码解析] TensorFlow 分布式环境(6) --- Master 动态逻辑

    [源码解析] TensorFlow 分布式环境(6) --- Master 动态逻辑 目录 [源码解析] TensorFlow 分布式环境(6) --- Master 动态逻辑 1. GrpcSess ...

  5. jQuery--筛选【过滤函数】

    之前选择器可以完成的功能,筛选也提供了相同的函数 筛选函数介绍 eq(index|-index) 类似:eq()index:正数,从头开始获得指定所有的元素,从0算起,0表示第一个-index:负数, ...

  6. memcached 是怎么工作的?

    Memcached 的神奇来自两阶段哈希(two-stage hash).Memcached 就像一 个巨大的.存储了很多<key,value>对的哈希表.通过 key,可以存储或查询任意 ...

  7. Redis 集群的主从复制模型是怎样的?

    为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所 以集群使用了主从复制模型,每个节点都会有 N-1 个复制品.

  8. linux java7升级到java8

    转自:https://blog.csdn.net/u010199866/article/details/81744382 linux java7升级到java8   版权 1.第一步先卸载所有老的jd ...

  9. 学习Apache(一)

    实验目的 通过apache实现反向代理的功能,类似nginx反向代理和haproxy反向代理 环境准备 逻辑架构如下 前端是apche服务器,监听80端口,后端有两台web服务器,分别是node1和n ...

  10. (stm32f103学习总结)—stm32中断系统

    一.NVIC 介绍 NVIC 英文全称是 Nested Vectored Interrupt Controller,中文意思就是嵌套向量中断控制器,它属于 M3 内核的一个外设,控制着芯片的中断相关功 ...