标签:基础类型 ble short 通过 summary 源代码 大量 binary isp
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(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(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
编写重载的扩展方法。
方法 |
说明 |
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
{
///
/// 未读取的数据
///
private ReadOnlySpan 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里大量应用。
dotnet高性能buffer
标签:基础类型 ble short 通过 summary 源代码 大量 binary isp
原文地址:https://www.cnblogs.com/kewei/p/14285873.html