- Unity Game Optimization
- Dr. Davide Aversa Chris Dickinson
- 644字
- 2021-06-24 12:13:03
Avoid retrieving string properties from GameObjects
Ordinarily, retrieving a string property from an object is the same as retrieving any other reference type property in C#; it should be acquired with no additional memory cost. However, retrieving string properties from GameObjects is another subtle way of accidentally crossing over the Native-Managed Bridge.
The two properties of GameObject affected by this behavior are tag and name. Therefore, it is unwise to use either property during gameplay, and you should only use them in performance-inconsequential areas, such as editor scripts. However, the tag system is commonly used for the runtime identification of objects, which can make this a significant problem for some teams.
For example, the following code would cause an additional memory allocation during every iteration of the loop:
for (int i = 0; i < listOfObjects.Count; ++i) {
if (listOfObjects[i].tag == "Player") {
// do something with this object
}
}
It is often a better practice to identify objects by their components and class types and to identify values that do not involve string objects, but sometimes we're forced into a corner. Maybe we didn't know any better when we started, we inherited someone else's code base, or we're using it as a workaround for something. Let's assume that, for whatever reason, we're stuck with the tag system, and we want to avoid the Native-Managed Bridge overhead cost.
Fortunately, the tag property is most often used in comparison situations, and GameObject provides the CompareTag() method, which is an alternative way to compare tag properties that avoids the Native-Managed Bridge entirely.
Let's perform a simple test to prove how this simple change can make all the difference:
void Update() {
int numTests = 10000000;
if (Input.GetKeyDown(KeyCode.Alpha1)) {
for(int i = 0; i < numTests; ++i) {
if (gameObject.tag == "Player") {
// do stuff
}
}
}
if (Input.GetKeyDown(KeyCode.Alpha2)) {
for(int i = 0; i < numTests; ++i) {
if (gameObject.CompareTag ("Player")) {
// do stuff
}
}
}
}
We can execute these tests by pressing the 1 and 2 keys to trigger the respective for loops. Here are the results:

Looking at the breakdown view for each spike, we can see two completely different outcomes:

Retrieving the tag property 10 million times (way more than makes sense in reality, but this is useful for comparison) results in about 400 megabytes of memory being allocated just for string objects alone. We can see this memory allocation happening in the spike within the GC Allocated element in the Memory Area of the Timeline View. Also, this process takes around 2,000 milliseconds to process, where another 400 milliseconds are spent on garbage collection once the string objects are no longer needed.
Meanwhile, using CompareTag() 10 million times costs around 1,000 milliseconds to process and causes no memory allocations, and hence no garbage collection. This is made apparent from the lack of a spike in the GC Allocated element in the Memory Area. This should make it abundantly clear that we must avoid accessing the name and tag properties whenever possible. If tag comparison becomes necessary, then we should make use of CompareTag(). Unfortunately, there is no equivalent for the name property, so we should stick to using tags where possible.
- Python量化投資指南:基礎、數據與實戰
- Visual C++串口通信開發入門與編程實踐
- 深入淺出Windows API程序設計:編程基礎篇
- C++ 從入門到項目實踐(超值版)
- 可解釋機器學習:模型、方法與實踐
- Service Mesh實戰:基于Linkerd和Kubernetes的微服務實踐
- ASP.NET程序開發范例寶典
- Citrix XenServer企業運維實戰
- Serverless Web Applications with React and Firebase
- STM8實戰
- Applied Deep Learning with Python
- Building Apple Watch Projects
- LibGDX Game Development By Example
- MATLAB從入門到精通
- JSP項目開發情境教程