【Unity优化】Unity字符串String优化
System.String
通过跟踪Unity的string,我们看到了Unity给我们提供的各种string接口。发现该代码来自mscorlib.dll,通过路径直接找到该dll
Editor\Data\MonoBleedingEdge\lib\mono\unity\mscorlib.dll
我们通过dnSpy这个工具来对mscorlib.dll进行反编译。打开dnSpy软件通过“文件/打开”找到该dll,将其加载进来。
根据string的命名空间直接找到“System”的“String”类
string.Format
通过反编译,我们看到string.Format在最终的实现过程中是重新new了一个StringBuilder。因此我们在对字符串进行Format操作的时候直接共用一个StringBuilder即可,具体实现如下
private static StringBuilder stringBuilder = new StringBuilder();
public static string Format(string src, params object[] args)
{
stringBuilder.Remove(0, stringBuilder.Length);
stringBuilder.AppendFormat(src, args);
return stringBuilder.ToString();
}
但是其实StringBuilder.Format的消耗也是挺大的,我们看看内部实现
最终调用的是AppendFormatHelper这个函数,里面实现也是极其复杂,这里也只是截取了一小部分,因此能不用Format的时候尽量不用Format,可以考虑使用Concat函数来代替。
string.Concat
string.Concat每次使用都会重新生成一个string,然后对其进行数据填充。当我们需要Concat的数据比较多的时候实际上是调用下面这个方法
里面每次都会判断填充的数据是否为null,循环体里都会有个object转string的操作,其实大可不必如此。既然我们之前已经有一个通用的StringBuilder了,可以直接公用即可,每次使用之前清除掉旧数据,实现循环使用。
public static string Concat(string s1,string s2)
{
stringBuilder.Remove(0, stringBuilder.Length);
stringBuilder.Append(s1);
stringBuilder.Append(s2);
return stringBuilder.ToString();
}
如果有多个参数s1,s2…就写多个Concat函数,如果在外部调用的时候遇到循环体可以在这边定义一个外部共享的StringBuilder,具体如下
private static StringBuilder shareStringBuilder = new StringBuilder();
public static StringBuilder GetShareStringBuilder()
{
shareStringBuilder.Remove(0, stringBuilder.Length);
return shareStringBuilder;
}
外部调用如下
public void Test()
{
StringBuilder stringBuilder = QString.GetShareStringBuilder();
for (int i = 0; i < 100; i++)
{
stringBuilder.Append(i);
}
Debug.Log(stringBuilder.ToString());
}
完整代码如下
using System.Text;
using UnityEngine;
/// <summary>
/// 字符串优化类
/// </summary>
public class QString
{
private static StringBuilder stringBuilder = new StringBuilder();
private static StringBuilder shareStringBuilder = new StringBuilder();
public static StringBuilder GetShareStringBuilder()
{
shareStringBuilder.Remove(0, stringBuilder.Length);
return shareStringBuilder;
}
public static string Format(string src, params object[] args)
{
stringBuilder.Remove(0, stringBuilder.Length);
stringBuilder.AppendFormat(src, args);
return stringBuilder.ToString();
}
public static string Concat(string s1, string s2)
{
stringBuilder.Remove(0, stringBuilder.Length);
stringBuilder.Append(s1);
stringBuilder.Append(s2);
return stringBuilder.ToString();
}
public static string Concat(string s1, string s2, string s3)
{
stringBuilder.Remove(0, stringBuilder.Length);
stringBuilder.Append(s1);
stringBuilder.Append(s2);
stringBuilder.Append(s3);
return stringBuilder.ToString();
}
}
public class QStringSample
{
public void Test()
{
StringBuilder stringBuilder = QString.GetShareStringBuilder();
for (int i = 0; i < 100; i++)
{
stringBuilder.Append(i);
}
Debug.Log(stringBuilder.ToString());
}
}
通过实验进行数据对比
using UnityEngine;
using System.Text;
using UnityEngine.Profiling;
public class QStringTest : MonoBehaviour
{
private void Update()
{
Before();
After();
}
string s1 = string.Empty;
private void Before()
{
Profiler.BeginSample("Before");
s1 = string.Empty;
for (int i = 0; i < 100; i++)
{
s1 += i;
}
Profiler.EndSample();
}
StringBuilder stringBuilder = QString.GetShareStringBuilder();
private void After()
{
Profiler.BeginSample("After");
stringBuilder.Remove(0, stringBuilder.Length);
for (int i = 0; i < 100; i++)
{
stringBuilder.Append(i);
}
Profiler.EndSample();
}
}
优化前的GC是25.2KB 优化后的GC只有2.9kB 执行时间也是之前的一半。