詳解C# Protobuf如何做到0分配內(nèi)存的序列化
題目很簡(jiǎn)單, 就是IMessage對(duì)象怎么變成Byte[]
答案1:
msg.ToByteArray()
這肯定不符合我們的要求
答案2:
using var memoryStream = new MemoryStream(); using var codedOutputStream = new CodedOutputStream(memoryStream); msg.WriteTo(codedOutputStream); codedOutputStream.Flush(); memoryStream.ToArray();
這里面memoryStream, codedOutputStream, 還有ToArray都產(chǎn)生了一個(gè)對(duì)象, MemoryStream內(nèi)部還會(huì)多產(chǎn)生一個(gè)byte[]對(duì)象
不符合要求
答案3:
有人說(shuō)你可以給MemoryStream傳遞一個(gè)byte[] slice, 讓MemoryStream直接用byte[]
var bytes = new byte[msg.CalculateSize()]; using var memoryStream = new MemoryStream(); using var codedOutputStream = new CodedOutputStream(memoryStream); msg.WriteTo(codedOutputStream); codedOutputStream.Flush();
這次消息直接被序列化到bytes里面去了, 但是memoryStream對(duì)象, codecOutputStream還有memoryStream內(nèi)部的byte[]都還在, 我就序列化了一個(gè)對(duì)象, 卻產(chǎn)生了3個(gè)垃圾對(duì)象
所以, 來(lái)仔細(xì)看看CodedOutputStream類(lèi):
/// <summary>
/// Creates a new CodedOutputStream that writes directly to the given
/// byte array. If more bytes are written than fit in the array,
/// OutOfSpaceException will be thrown.
/// </summary>
public CodedOutputStream(byte[] flatArray) : this(flatArray, 0, flatArray.Length)
{
}
/// <summary>
/// Creates a new CodedOutputStream that writes directly to the given
/// byte array slice. If more bytes are written than fit in the array,
/// OutOfSpaceException will be thrown.
/// </summary>
private CodedOutputStream(byte[] buffer, int offset, int length)
{
this.output = null;
this.buffer = buffer;
this.position = offset;
this.limit = offset + length;
leaveOpen = true; // Simple way of avoiding trying to dispose of a null reference
}
提供了一個(gè)byte[]的構(gòu)造函數(shù), 但是沒(méi)提供slice的構(gòu)造函數(shù), 好在有一個(gè)私有的構(gòu)造函數(shù)
答案4:
這邊就不寫(xiě)代碼了, 大概意思就是通過(guò)反射私有構(gòu)造函數(shù)來(lái)構(gòu)造一個(gè)CodedOutputStream對(duì)象, 來(lái)省掉MemoryStream和他內(nèi)部的byte[]
現(xiàn)在離答案已經(jīng)比較接近了
那我們的問(wèn)題是, 能不能連CodedOutputStream也省掉呢?
答案5來(lái)了:
經(jīng)過(guò)仔細(xì)觀察, 發(fā)現(xiàn)這個(gè)類(lèi)沒(méi)有使用Stream的情況下, 就只需要修改buffer, limit, 和position幾個(gè)成員就行了, 雖然是private成員, 但是C#還是能修改
下來(lái)立馬實(shí)踐
delegate void ClearCodedOutputStream(CodedOutputStream stream, byte[] buffer, int offset, int count);
static ClearCodedOutputStream ResetCodedOutputStream;
static CodedOutputStream codedOutputStream = new CodedOutputStream(new byte[10]);
static unsafe void Encode(IMessage msg, byte[] buffer)
{
ResetCodedOutputStream(codedOutputStream, buffer, 0, buffer.Length);
msg.WriteTo(codedOutputStream);
codedOutputStream.Flush();
}
static Action<T, TValue> MakeSetter<T, TValue>(FieldInfo field)
{
DynamicMethod m = new DynamicMethod(
"setter", typeof(void), new Type[] { typeof(T), typeof(TValue) }, typeof(Program));
ILGenerator cg = m.GetILGenerator();
cg.Emit(OpCodes.Ldarg_0);
cg.Emit(OpCodes.Ldarg_1);
cg.Emit(OpCodes.Stfld, field);
cg.Emit(OpCodes.Ret);
return (Action<T, TValue>)m.CreateDelegate(typeof(Action<T, TValue>));
}
static void Main(string[] args)
{
var bufferField = typeof(CodedOutputStream).GetField("buffer", BindingFlags.NonPublic | BindingFlags.Instance);
var limitField = typeof(CodedOutputStream).GetField("limit", BindingFlags.NonPublic | BindingFlags.Instance);
var positionField = typeof(CodedOutputStream).GetField("position", BindingFlags.NonPublic | BindingFlags.Instance);
var setLimit = MakeSetter<CodedOutputStream, int>(limitField);
var setPosition = MakeSetter<CodedOutputStream, int>(positionField);
var setBuffer = MakeSetter<CodedOutputStream, byte[]>(bufferField);
ResetCodedOutputStream = (stream, buffer, offset, length) =>
{
//this.buffer = buffer;
//this.position = offset;
//this.limit = offset + length;
setBuffer(stream, buffer);
setPosition(stream, offset);
setLimit(stream, offset + length);
};
var buffer = new byte[msg.CalculateSize()];
Encode(msg, buffer);
}
這個(gè)實(shí)例代碼里面, 用了一個(gè)static的全局CodedOutputStream, 真正用的時(shí)候, 肯定要保證線程安全.
所以接下來(lái)的問(wèn)題是:
1. 如何保證CodedOutputStream對(duì)象線程安全
2. 如何把var buffer = new byte[msg.CalculateSize()];這個(gè)也省掉
這倆問(wèn)題就留給讀者思考.
Github: http://github.com/egmkang
到此這篇關(guān)于詳解C# Protobuf如何做到0分配內(nèi)存的序列化的文章就介紹到這了,更多相關(guān)C# Protobuf 序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#中OpenCVSharp實(shí)現(xiàn)輪廓檢測(cè)
這篇文章主要介紹了C#中OpenCVSharp實(shí)現(xiàn)輪廓檢測(cè),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
在C#中使用二叉樹(shù)實(shí)時(shí)計(jì)算海量用戶(hù)積分排名的實(shí)現(xiàn)詳解
這篇文章主要介紹了在C#中使用二叉樹(shù)實(shí)時(shí)計(jì)算海量用戶(hù)積分排名的實(shí)現(xiàn)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
深入多線程之:內(nèi)存柵欄與volatile關(guān)鍵字的使用分析
本篇文章對(duì)內(nèi)存柵欄與volatile關(guān)鍵字的使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05

