zoukankan      html  css  js  c++  java
  • dotnet高性能buffer

    1 前言

    我曾经写过《杂谈.netcore的Buffer相关新类型》的博客,简单介绍过BinaryPrimitives、Span<>,Memory<>,ArrayPool<>,Memorypool<>这些基础类型,在实际项目中,我们更需要的是更上层的高效缓冲区申请、buffer写入、buffer读取功能。本文将介绍如何利用这些基础类型,封装成易于使用的buffer相关操作类,这些类的源代码在MemoryExtensions库里。

    2 buffer知识

    buffer的申请

    通过经验与实验数据,根据不同场景与buffer大小,选择合适的申请方式。

    申请式 特点 局限
    stackalloc byte 非常快速 堆栈上分配内存块,容量小且在方法返回时缓冲区丢弃
    new byte[] 当小于1KB时速度快 频繁创建导致内存碎片,GC压力大
    ArrayPool.Rent 适合大的缓冲区租赁,几乎无内存分配 缓冲区小于1KB时,租赁不如new来得快

    IBufferWriter接口

    此接口支持获取缓冲区的写入Span或GetMemory给外部直接写入数据,写入完成之后调用Advance(int)方法,告诉writer实际的写入大小。

    我们来对比一下MemoryStream的Write()方法,比如要写一个int类型的值,我们不得不将int转为4字节的byte[],然后传byte[]到Write()方法。这个4字节的byte[]是一个副作用,它的存在原于外部无法获取和扩大MemoryStream的缓冲区。

    3 BufferWriter的实现

    根据“buffer的申请”几种方式,我们实现多种不同的BufferWriter。

    RecyclableBufferWriter

    可回收的自动扩容BufferWriter,适合于大的缓冲区的场景。它的缓冲区通过ArrayPool来租赁,用完之后,要Dispose()归还到ArrayPool。优点是内存分配少,缺点是租赁比直接创建小的缓冲区还要慢。

    var writer = new RecyclableBufferWriter<byte>(4);
    writer.Write((byte)1);
    writer.Write(new byte[] { 2, 3, 4 });
    writer.WriteBigEndian(int.MaxValue);           
    var writtern = writer.WrittenSpan; // 1,2,3,4,127,255,255,255
    
    // return the buffer to pool
    writer.Dispose();
    

    ResizableBufferWriter

    自动扩容的BufferWriter,适合小的动态缓冲区的场景。它的冲区通过new Array来创建,通过Array.Resize扩容。优点是cpu性能好,缺点是内存分配高。

    var writer = new ResizableBufferWriter<byte>(4);
    writer.Write((byte)1);
    writer.Write(new byte[] { 2, 3, 4 });
    writer.WriteBigEndian(int.MaxValue);           
    var writtern = writer.WrittenSpan; // 1,2,3,4,127,255,255,255
    

    FixedBufferWriter

    固定大小缓冲区,就是我们自己new的Array,包装为IBufferWriter对象。

    var array = new byte[16];
    
    var writer = array.CreateWriter();
    writer.WriteBigEndian(18);
    writer.WriteBigEndian(2.01f);
    

    4 IBufferWriter的扩展

    经常会遇到将int、double等诸多数字类型写入IBufferWriter的场景,期间还涉及平台的BigEndian或LittleEndian,我们给IBufferWriter<byte>编写重载的扩展方法。

    方法 说明
    WriteBigEndian(this IBufferWriter, short) short
    WriteBigEndian(this IBufferWriter, int) int
    WriteBigEndian(this IBufferWriter, long) long
    WriteBigEndian(this IBufferWriter, ushort) ushort
    WriteBigEndian(this IBufferWriter, uint) uint
    WriteBigEndian(this IBufferWriter, ulong) ulong
    WriteBigEndian(this IBufferWriter, float) float
    WriteBigEndian(this IBufferWriter, double) double
    WriteLittleEndian(this IBufferWriter, short) short
    WriteLittleEndian(this IBufferWriter, int) int
    WriteLittleEndian(this IBufferWriter, long) long
    WriteLittleEndian(this IBufferWriter, ushort) ushort
    WriteLittleEndian(this IBufferWriter, uint) uint
    WriteLittleEndian(this IBufferWriter, ulong) ulong
    WriteLittleEndian(this IBufferWriter, float) float
    WriteLittleEndian(this IBufferWriter, double) double

    5 ref BufferReader

    同样的,我们也经常遇到从缓冲区中读取为int、double等诸多数字类型的场景,所以也需要设计一个高效的BufferReader。

    public ref struct BufferReader
    {
        /// <summary>
        /// 未读取的数据
        /// </summary>
        private ReadOnlySpan<byte> span;
    }
    

    给它设计ReadLittleEndian和ReadBigEndian相关Api

    方法 说明
    ReadBigEndian(out short) short
    ReadBigEndian(out int) int
    ReadBigEndian(out long) long
    ReadBigEndian(out ushort) ushort
    ReadBigEndian(out uint) uint
    ReadBigEndian(out ulong) ulong
    ReadBigEndian(out float) float
    ReadBigEndian(out double) double
    ReadLittleEndian(out short) short
    ReadLittleEndian(out int) int
    ReadLittleEndian(out long) long
    ReadLittleEndian(out ushort) ushort
    ReadLittleEndian(out uint) uint
    ReadLittleEndian(out ulong) ulong
    ReadLittleEndian(out float) float
    ReadLittleEndian(out double) double

    6 关于MemoryExtensions库

    本文提到的这些类或结构体,在MemoryExtensions库里都有实现,可以直接使用,其中BufferWriter技术已经在WebApiClient里大量应用。

  • 相关阅读:
    xunjian.sh
    192.168.50.235配置
    自动备份并删除旧日志
    bg和fg命令
    linux之sed用法
    正则表示第二行,第二列
    linux下redis安装
    Hma梳理
    linux 系统监控、诊断工具之 lsof 用法简介
    java的基本数据类型有八种
  • 原文地址:https://www.cnblogs.com/kewei/p/14285873.html
Copyright © 2011-2022 走看看