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里大量应用。
dotnet高性能buffer的更多相关文章
- [2018-11-03]2018年10月28日宁波dotnet社区活动回顾及下次活动预告
离上次活动,有半年了,汗.之后尽量保证每月一次,以组织为主,多邀请嘉宾来分享. 本次活动不足之处 人手不足:由于活动组织事项受限于人手(目前就我一个,这次活动前后我又应邀给大红鹰学院应届生介绍dotn ...
- 开源Influxdb2高性能客户端
前言 最近我在了解时序数据库Influxdb 2.x版本,体验一翻之后,感觉官方的出品的.net客户端还有很多优化的地方,于是闭关几天,不吃不喝,将老夫多年练就的高性能网络通讯与高性能Buffer操作 ...
- .NET 2.0 参考源码索引
http://www.projky.com/dotnet/2.0/Microsoft/CSharp/csharpcodeprovider.cs.htmlhttp://www.projky.com/do ...
- 线程安全使用(四) [.NET] 简单接入微信公众号开发:实现自动回复 [C#]C#中字符串的操作 自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化 自已动手做高性能消息队列 自行实现高性能MVC WebAPI 面试题随笔 字符串反转
线程安全使用(四) 这是时隔多年第四篇,主要是因为身在东软受内网限制,好多文章就只好发到东软内部网站,懒的发到外面,现在一点点把在东软写的文章给转移出来. 这里主要讲解下CancellationT ...
- 使用Ring Buffer构建高性能的文件写入程序
最近常收到SOD框架的朋友报告的SOD的SQL日志功能报错:文件句柄丢失.经过分析得知,这些朋友使用SOD框架开发了访问量比较大的系统,由于忘记关闭SQL日志功能所以出现了很高频率的日志写入操作,从而 ...
- dotnet core 使用 MongoDB 进行高性能Nosql数据库操作
好久没有写过Blog, 每天看着开源的Java社区流口水, 心里满不是滋味. 终于等到了今年六月份 dotnet core 的正式发布, 看着dotnet 社区也一步一步走向繁荣, 一片蒸蒸日上的大好 ...
- Python使用Zero-Copy和Buffer Protocol实现高性能编程
无论你程序是做什么的,它经常都需要处理大量的数据.这些数据大部分表现形式为strings(字符串).然而,当你对字符串大批量的拷贝,切片和修改操作时是相当低效的.为什么? 让我们假设一个读取二进制数据 ...
- Netty使用Google Protocol Buffer完成服务器高性能数据传输
一.什么是Google Protocol Buffer(protobuf官方网站) 下面是官网给的解释: Protocol buffers are a language-neutral, platfo ...
- DotNet 资源大全中文版,内容包括:编译器、压缩、应用框架、应用模板、加密、数据库、反编译、IDE、日志、风格指南等
DotNet 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列的资源整理.awesome-dotnet 是由 quozd 发起和维护.内容包括:编译器. ...
随机推荐
- Codeforces Edu Round 48 A-D
A. Death Note 简单模拟,可用\(\%\)和 \(/\)来减少代码量 #include <iostream> #include <cstdio> using nam ...
- 关于微信NFC功能开发的链接总结
特此申明:若有侵权,请联系我,我会第一时间删除 一. 小程序开发一般流程: 首先调用 wx.getHCEState(OBJECT), 判断设备是否支持NFC,(ios,android兼容性处理) 调用 ...
- JApiDocs(自动生成接口文档神器)
JApiDocs教程 前言 作为一名优秀的程序员来说,由于涉及到要与前端进行对接,所以避免不了的就是写接口文档.写完接口文档,一旦代码返回结果,参数等出现变动,接口文档还得随之改动,十分麻烦,违背了我 ...
- adb devices查看不到Android模拟器的设备
1.先开启模拟器,后执行adb devices命令: List of devices attached 2.找到SDK的安装目录下platform-tools文件夹下adb.exe文件,将其复制到桌面 ...
- vue中的样式绑定
样式绑定 样式绑定有class绑定和style绑定,这里我们分别说说 class绑定 class样式绑定与有两种语法:对象语法(v-bind:class='{active:isActive}').数组 ...
- 微服务开发的最大痛点-分布式事务SEATA入门简介
前言 在微服务开发中,存在诸多的开发痛点,例如分布式事务.全链路跟踪.限流降级和服务平滑上下线等.而在这其中,分布式事务是最让开发者头痛的.那分布式事务是什么呢? 分布式事务就是指事务的参与者.支持事 ...
- SQL注入-DNS注入(二)
其实就是盲注的简化版本,不过这种方式确实简单,不需要写py脚本一点点去跑 参考文章: https://www.jianshu.com/p/c805209244c2 这 ...
- JavaWeb基础总结:Servlet专题
最近工作中有部分整改老接口的任务,大部分与Spring的拦截器,Tomcat相关,改到一些底层的代码发现,对基础J2EE的知识有些遗忘,需要频繁查阅,索性从头系统的整理一下Servlet和Filter ...
- 安装篇四:安装NGINX(1.4.0版本)
#1.NGINX安装 1.安装文件上传软件 [root@TEST ~]# yum install lrzsz –y <---拖拽文件 2.检查软件安装的系统环境 [root@TEST ~]# c ...
- ReentrantLock锁-CAS与阻塞
ReentrantLock锁 ReentrantLock通过原子操作和阻塞实现锁原理,一般使用lock获取锁,unlock释放锁 lock的时候可能被其他线程获得所,那么此线程会阻塞自己,关键原理底层 ...