Unity/ C#:优化字符串操作产生的GC【Using ZString】

前言:

最近被要求改关于字符串操作中的GC,比如:数字转文本、字符串拼接等。一般来讲有的可以避免,但有的很难避免。比如Number To String的操作,总归是写在逻辑的硬需求里面的(等级、时间、飘血等)。

如上图所示,这个是Int32.ToString的时候产生的GC,其他的数值转文本也差不多一个情况。即便调用其他的API,最终都会走到这边一步,一个String.InternalAllocateStr就会产生约42B的GC出来。当然这个是可以考虑作为优化的点,但是这个方法属于C#底层了,我们需要对String进行扩充来避免他的GC情况出现。

这个时候,通过朋友的介绍,发现了一个叫ZString的东西,他的出现就是为了避免这种GC的问题,于是就开心地拿来测试了一下。

 

ZString:

关于ZString的介绍和源代码见下面的链接, 这里就不赘述了。

介绍:https://coh5.cn/p/1ace6338.html

GitHub:https://github.com/871041532/zstring

 

1、使用:

这个ZString的使用方法较为麻烦,自然不能像C#的String那么自如。例如一个将Int转为字符串的操作,需要这么写:

                using (ZString.Block())
                {
                    int number = 16578187;
                    ZString zStr = number;
                    string str2 = zStr.ToString();
                }

对的,他在操作的时候要先把这个东西进行Block操作,而出了这个作用域它的值有可能会被改变。

注意:不可使用ZString作为类的成员变量,不建议在Using作用域中写超大For循环。

首次调用时会初始化类,分配各种空间,建议游戏启动时调用一次using(ZString.block()){}

//使用方式:
// 1.当update每帧刷新标签显示,或者该字符串是短时间使用的则使用如下方式:
        using (ZString.Block())
        {
            ZString a="hello world";
            a.text=a
        }
// 此方式设置的string值位于深拷贝缓存中,一定时间可能会改变。

//2.比如路径,或者面板上常驻的字符串。需要常驻的则需要intern一下在作用域外使用

    using (ZString.Block())
        {
            ZString a="Assets/";
            ZString b=a+"prefabs/"+"/solider.prefab";
            a.path=b.Intern();
        }
//此方式设置的string值位于intern表中,游戏运行期间不会改变。

 

警告:Unity UGUI的Text组件不能直接使用上述方法赋值!!!

无论是使用 aText.text=aZString.toString(),还是aText.text=aZString都会造成设置值和显示值不一致。虽然在本帧打印出来的值都是正确的,但是实际显示的值是不对的(实际上只有字符串长度是正确的),如下代码:

其跑出来的结果如下:

可见在本帧怎么 样看都是对的,但实际上Text上显示的文本确实是个诡异的错值。

修正方法:用ZString.Intern()取代ZString.ToString();

 

警告2:即便用上述方法进行修正,虽然解决了显示文本不对的问题,但是却因为Intern方法产生了GC!因为其底层调用了New String方法:

如图,当你的数字是从来没有出现过的时候,就会产生一个新的字符串,此时就会导致32B的GC。而这个原因归根结底就是因为Text.text被修改后,Unity并没有立即修改文本显示(感觉上是这样)。虽然看了源码也没发现有什么地方不对,但实际上就是这种效果。

后来苦思冥想想不通,用了个很弱智的方法来解决:

在每一次对Text进行赋值后,同时更改Text的颜色。其颜色用2个颜色交替,一个是原来的颜色,另一个是比原来的颜色其透明度略低0.005的颜色(表现上基本看不出差别);或者修改文本框的长宽。

至于为什么,只能说能力有限了……

 

2、效率测试:

string和ZString的NumberToString对比:

string :总计1ms,8.6KB GC;

ZString:总计约1.14+0.98+0.48=2.6ms,0GC;

 

字符串拼接测试:

String:    总计0.48ms,8.6 kb GC;

ZString:  总计2.4ms,0 GC;

 

结论:

这个ZString算是所有的字符串补充方案里面优化得最好的了,但是比起string来单说速度而言有不小的差距。在进行NumberToString操作的时候,其速度是原生String的1/3;而在进行字符串拼接的时候,速度降到1/6 。string即便是在有40B的GC的情况下,其总体速度还是要快上不少。

但好在没有GC,所以还是各位看自己的项目情况使用吧。

 

警告:如果直接使用GitHub下载下载的那个ZString来使用,虽然电脑上没什么毛病,但是打包成安卓如果用了IL2CPP就会报错导致打包失败!

Failed running C:\Program Files\Unity\Editor\Data\il2cpp/build/il2cpp.exe 

use of undeclared identifier

从报错情况下来看是因为一些结构体没有定义导致的,而源码的结构体都是写在类外面的。所以要做的就是把这些结构体移动到类里面(反正也没别的地方要用):

这样就可以了,亲测 可用~

还看到一个帖子:https://blog.csdn.net/qqqqqq8633542/article/details/80654218?utm_source=blogxgwz3;说是把ZString放在Plugins目录下也可以。不过他说的是XCode编译不过,我想应该是差不多一样的问题。

 

 

 

 

最后,感谢下海的某位骚骚的大佬~~~~

©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页