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里大量应用。

dotnet高性能buffer的更多相关文章

  1. [2018-11-03]2018年10月28日宁波dotnet社区活动回顾及下次活动预告

    离上次活动,有半年了,汗.之后尽量保证每月一次,以组织为主,多邀请嘉宾来分享. 本次活动不足之处 人手不足:由于活动组织事项受限于人手(目前就我一个,这次活动前后我又应邀给大红鹰学院应届生介绍dotn ...

  2. 开源Influxdb2高性能客户端

    前言 最近我在了解时序数据库Influxdb 2.x版本,体验一翻之后,感觉官方的出品的.net客户端还有很多优化的地方,于是闭关几天,不吃不喝,将老夫多年练就的高性能网络通讯与高性能Buffer操作 ...

  3. .NET 2.0 参考源码索引

    http://www.projky.com/dotnet/2.0/Microsoft/CSharp/csharpcodeprovider.cs.htmlhttp://www.projky.com/do ...

  4. 线程安全使用(四) [.NET] 简单接入微信公众号开发:实现自动回复 [C#]C#中字符串的操作 自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化 自已动手做高性能消息队列 自行实现高性能MVC WebAPI 面试题随笔 字符串反转

    线程安全使用(四)   这是时隔多年第四篇,主要是因为身在东软受内网限制,好多文章就只好发到东软内部网站,懒的发到外面,现在一点点把在东软写的文章给转移出来. 这里主要讲解下CancellationT ...

  5. 使用Ring Buffer构建高性能的文件写入程序

    最近常收到SOD框架的朋友报告的SOD的SQL日志功能报错:文件句柄丢失.经过分析得知,这些朋友使用SOD框架开发了访问量比较大的系统,由于忘记关闭SQL日志功能所以出现了很高频率的日志写入操作,从而 ...

  6. dotnet core 使用 MongoDB 进行高性能Nosql数据库操作

    好久没有写过Blog, 每天看着开源的Java社区流口水, 心里满不是滋味. 终于等到了今年六月份 dotnet core 的正式发布, 看着dotnet 社区也一步一步走向繁荣, 一片蒸蒸日上的大好 ...

  7. Python使用Zero-Copy和Buffer Protocol实现高性能编程

    无论你程序是做什么的,它经常都需要处理大量的数据.这些数据大部分表现形式为strings(字符串).然而,当你对字符串大批量的拷贝,切片和修改操作时是相当低效的.为什么? 让我们假设一个读取二进制数据 ...

  8. Netty使用Google Protocol Buffer完成服务器高性能数据传输

    一.什么是Google Protocol Buffer(protobuf官方网站) 下面是官网给的解释: Protocol buffers are a language-neutral, platfo ...

  9. DotNet 资源大全中文版,内容包括:编译器、压缩、应用框架、应用模板、加密、数据库、反编译、IDE、日志、风格指南等

    DotNet 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列的资源整理.awesome-dotnet 是由 quozd 发起和维护.内容包括:编译器. ...

随机推荐

  1. STL——容器(Map & multimap)的拷贝构造与赋值

    1. Map & multimap 的拷贝构造与赋值 map(const map &mp);               //拷贝构造函数 map& operator=(con ...

  2. C++异常之七 标准库里的异常类

    标准库里的异常类 C++标准提供了一组标准异常类,这些类以基类 Exception 开始,标准程序库抛出的所有异常,都派生于该基类,这些类构成如图所示的异常类的派生继承关系,该基类提供一个成员函数 w ...

  3. 在Nuxt中使用react-id-swiper封装公共的轮播图组件(移动端

    首先就是引入swiper import Swiper from 'react-id-swiper': 一个轮播图首先要考虑到一种情况就是当只有一张图的时候是不是需要按轮播图来处理 一般情况下,一张图是 ...

  4. python 通过pip freeze、dowload打离线包及自动安装【适用于保密的离线环境】

    python的pip是其包管理工具,相当方便好用.本文只介绍pip 如何通过其freeze命令打离线包,及其离线包的安装脚本.这个知识点,特别适用于不适合连通互联网,设备需要物理隔绝,保密要求严格的客 ...

  5. c++日常小问题

    语法解析问题. 当给一个变量构造函数传递一个临时变量,而不是命名的变量,c++编译器会将其解析为函数声明,而不是类型对象的定义. class hello { void operator()() { s ...

  6. 多任务-python实现-使用生成器完成多任务(2.1.14)

    @ 目录 1.注意 2. 代码 关于作者 1.注意 先定义函数,函数里面放一个yiled 主函数生成该对象 执行while循环 调用生成器对象的next 因为每次调用next,不会继续执行,从而 完成 ...

  7. php代码审计整理

    目录 变量覆盖 1x01.extract 变量覆盖 定义和用法 语法 漏洞产生:使用了默认设置 攻击方法:制造变量名冲突,对于需要相等的值可以同时置空 修复:设定一个冲突时的处理规则 例题: 1x02 ...

  8. Python制作塔防小游戏

    开发工具 Python版本:3.6.4 相关模块: pygame模块: 以及一些Python自带的模块.

  9. ASP.NET Core 3.1使用Swagger API接口文档

    Swagger是最流行的API开发工具,它遵循了OpenAPI规范,可以根据API接口自动生成在线文档,这样就可以解决文档更新不及时的问题.它可以贯穿于整个API生态,比如API的设计.编写API文档 ...

  10. (四)linux的常用环境变量及设置

    一.为什么要设置环境变量 1.环境变量能解决什么问题? 你是否经历过输入$python命令后,屏幕上打印出python:command not found的尴尬:每一次都要输入$/home/tools ...