V4002. Unity Engine. Avoid storing consecutive concatenations inside a single string in performance-sensitive context. Consider using StringBuilder to improve performance.
The analyzer has detected the opportunity to optimize concatenation operations inside a frequently called method.
The concatenation causes the creation of a new string object. As a result, extra memory is allocated in a managed heap. To improve performance, you need to avoid concatenations inside frequently executed code. If you need to repeatedly add various fragments to a string value, the Unity developers recommend to use the 'StringBuilder' type instead of concatenation.
Consider the example:
[SerializeField] Text _stateText;
....
void Update()
{
....
string stateInfo = ....;
....
stateInfo += ....;
stateInfo += ....;
....
stateInfo += ....;
_stateText.text = stateInfo;
....
}
Here the construction of the 'stateInfo' string is implemented by several concatenation operations. Executing the code in the 'Update' method (which is called several dozen times per second), you will get rapid accumulation of 'garbage' in memory and that's why the garbage collector is frequently called to clean it up. Calling the garbage collector lots of times can have a negative impact on the performance. You can avoid extra memory allocation by using the 'StringBuilder' object:
[SerializeField] Text _stateText;
....
StringBuilder _stateInfo = new StringBuilder();
void Update()
{
_stateInfo.Clear();
....
_stateInfo.AppendLine(....);
_stateInfo.AppendLine(....);
....
_stateInfo.AppendLine(....);
_stateText.text = _stateInfo.ToString();
....
}
The 'Clear' method clears the 'StringBuilder' content, but does not release the allocated memory. Thus, the extra memory allocation will be required only if the already used memory is insufficient to store the new text.
Consider another example:
[SerializeField] Text _text;
....
List<string> _messages = new();
....
void LateUpdate()
{
....
string message = BuildMessage();
_text.text = message;
_messages.Clear();
}
string BuildMessage()
{
string result = "";
foreach (var msg in _messages)
result += msg + "\n";
return result;
}
In this example, the 'BuildMessage' method helps generate the message displayed on the interface. Since this method is called inside 'LateUpdate' (as often as inside 'Update'), it's worth optimizing too:
StringBuilder _message = new StringBuilder();
string BuildMessage()
{
_message.Clear();
foreach (var msg in _messages)
_message.AppendLine(msg);
return _message.ToString();
}