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

版权声明:本文为博主原创文章,欢迎转载。请保留博主链接:http://blog.csdn.net/andrewfan https://blog.csdn.net/AndrewFan/article/details/62227628

序列化

很多时候,我们可以通过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来的高。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页