作者:所谓-旧 | 来源:互联网 | 2023-09-10 13:40
前言:最近被要求改关于字符串操作中的GC,比如:数字转文本、字符串拼接等。一般来讲有的可以避免,但有的很难避免。比如NumberToString的操作,总归是写在逻辑的硬需求里面的
前言:最近被要求改关于字符串操作中的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编译不过,我想应该是差不多一样的问题。
最后,感谢下海的某位骚骚的大佬~~~~