研究背景:之所以研究这个呢,看过我上一篇文章的人应该知道,对项目的socket网络部分进行优化,主要优化点之一就是建立对象池,复用byte Array,之前还有个可以优化的部分没有优化,就是因为在用protobuf发送数据对象时,要把Imessage转成byte发送,也就是调用 ToByteArray方法
public static byte[] ToByteArray(this IMessage message)
{
ProtoPreconditions.CheckNotNull(message, "message");
byte[] array = new byte[message.CalculateSize()];
CodedOutputStream codedOutputStream = new CodedOutputStream(array);
message.WriteTo(codedOutputStream);
codedOutputStream.CheckNoSpaceLeft();
return array;
}
已经标记出来了,每次发送都要调用一次ToByteArray,内部又会分配一个byte数组作为转换的结果并返回,凡是有new的地方是不是就可以想办法用内存池?是的,在这一步看起来没啥问题,我可以仿照这里的写法,自定义一个类似的拓展方法,如下
public static byte[] ToByteArrayOptimiszed(this IMessage message)
{
ProtoPreconditions.CheckNotNull(message, "message");
//byte[] array = new byte[message.CalculateSize()];
byte[] array = arraypool.rent(message.CalculateSize());
CodedOutputStream codedOutputStream = new CodedOutputStream(array);
message.WriteTo(codedOutputStream);
codedOutputStream.CheckNoSpaceLeft();
return array;
}
这个arraypool是ArrayPool<byte>.shared对象,.net提供的一个线程安全的内存池,rent方法返回最小大于等于你申请的大小的2的幂次方的内存大小,比如申请10,实际返回16,提供具体看msdn ArrayPool 这下一步所遇到的问题就是CodedOutputStream codedOutputStream = new CodedOutputStream(array);这一行需要修改成指定下标和长度的构造函数,因为不是整个array都是有效数据段,所幸提供了这个构造函数,但是不幸的是,这个构造方法是private的
public CodedOutputStream(byte[] flatArray)
: this(flatArray, 0, flatArray.Length)
{
}private CodedOutputStream(byte[] buffer, int offset, int length)
{
output = null;
this.buffer = ProtoPreconditions.CheckNotNull(buffer, "buffer");
state.position = offset;
state.limit = offset + length;
WriteBufferHelper.Initialize(this, out state.writeBufferHelper);
leaveOpen = true;
}
但是但是但是!!!重要的事情说三遍,protobuf是开源的,那么我可以拉下来代码修改然后打包dll,再用到工程中,说干就干,过程中遇到了几个问题,主要参考了以下两篇文章,第一篇是如何打包protobuf成dll。ProtoBuf编译生成Google.Protobuf.dll与Protoc.exe、自定义生成代码规则-CSDN博客
第二篇是发现unity导入dll报错,参考文章Unity导入Goolgle.Protobuf.dll报错_unable to resolve reference 'assembly-csharp'. is -CSDN博客
1.Assembly ‘Library/ScriptAssemblies/Assembly-CSharp.dll’ will not be loaded due to errors:
Reference has errors ‘Google.Protobuf’.
2.Assembly ‘Assets/Plugins/Google.Protobuf.dll’ will not be loaded due to errors:
Unable to resolve reference ‘System.Runtime.CompilerServices.Unsafe’. Is the assembly missing or incompatible with the current platform?
Reference validation can be disabled in the Plugin Inspector.
把打包生成的文件夹里面的其他依赖dll一同导入,问题解决,完美work。
最后,虽然问题是用ArrayPool导致的,但是,用现成的轮子总比重复造轮子要好,而且自己造的轮子也不一定有人家的好,本着学习的目的,可以在github查看arraypool的源码。