【Unity优化】为C#定制联合(Union)提高序列化速度
发表于2018-10-10
很多时候,我们可以通过Protobuf之类的插件来执行序列化处理,当然也可以自己尝试自己手动处理序列化,只要针对数据对象和字节数组之间可以互相转化就可以了。唯一要考虑到问题就是在自己手写序列化的过程中,怎样转换你的数据,比如最基本的整形、浮点型这些。下面就来看看使用联合(Union)提高序列化速度的方法。
BitConvertor
在Unity中,有BitStream,然而好像没有什么用处,它们好像与Unet密切相关,而Unet目前生死未卜。
在CSharp中默认为我们提供了一个BitConvertor方法。通过它可以进行基础数据类型和字节数组之间的转换。然而我并不推荐你用它,这里我简单做了一个测试来解释原因。以下的代码都是放在一个组件的Update方法中去执行。
void testConvertFloatToBytes() { float f = 10; for (int i = 0; i < 1000; i++) { BitConverter.GetBytes(f); } }
这个方法在做从float到byte数组的转换。然而,你应该能看出来一些问题。它在不停制造出byte[],而数组都是个对象,因此它在不断产生GC Alloc。
如下图:
因此,当在需要连续不断进行序列化的时刻(网络游戏中),这个方法是不可以使用的。
使用联合(Union)
在CSharp中默认是没有联合(Union)的,然而我们可以构建出一个类似联合的结构体。
使用结构体布局(StructLayout)属性以及字段偏移(FieldOffset)属性就可以自己定制数据在结构体中的分布,从而定制出一个联合。
[StructLayout(LayoutKind.Explicit, Size = 4)] struct UNum { [FieldOffset(0)] public byte b0; [FieldOffset(1)] public byte b1; [FieldOffset(2)] public byte b2; [FieldOffset(3)] public byte b3; [FieldOffset(0)] public int i; [FieldOffset(0)] public float f; }
此时,我们进行数据转换时,可以使用以下的方式进行转换:
protected byte[] m_datas; protected int m_dReader; void testConvertFloatToBytes_U() { float f = 10; for (int i = 0; i < 1000; i++) { UNum u = new UNum(); u.f = f; m_datas[m_dReader++] = u.b0; m_datas[m_dReader++] = u.b1; m_datas[m_dReader++] = u.b2; m_datas[m_dReader++] = u.b3; } }
这个方法不会产生任何的GC,因为它没有将结果放在小数组中,而是直接放入了一个整体缓冲区。
注意m_datas需要是足够大的缓冲区,m_dReader是缓冲区读取索引。
总结
当执行连续大量的序列化时,使用联合可以很好地避免GCAlloc产生,而且效率要比BitConvertor来的高。来自:https://blog.csdn.net/AndrewFan/article/details/62227628