【Unity优化】为C#定制联合(Union)提高序列化速度

来源:互联网 发布:淘宝客怎么关闭掉 编辑:程序博客网 时间:2024/06/03 23:50

序列化

很多时候,我们可以通过Protobuf之类的插件来执行序列化处理,不过如果你像我一样,对代码有洁癖的话,可能就会担心这些第三方插件的处理效率问题,有时候就会尝试自己手动处理序列化。还有一种情况就是,如果你的游戏是实时联网的,需要不断网络IO,此时还是保持洁癖比较好。
其实自己手动处理序列化也不是非常麻烦。只要针对数据对象和字节数组之间可以互相转化就可以了。
那么在自己手写序列化的过程中,遇到的一个问题就是怎样转换你的数据,比如最基本的整形、浮点型这些。

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。
如下图:
BitConverter.GetBytes

因此,当在需要连续不断进行序列化的时刻(网络游戏中),这个方法是不可以使用的。

使用联合(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来的高。

0 0
原创粉丝点击