【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 執行時間也是之前的一半。