The performance optimization of a project revolves around the CPU, GPU, and memory in three major ways.
Memory management is a priority in the development phase, whether it is a game or a VR application.
However, in the large number of projects we have evaluated, more than 90% of the projects have varying degrees of memory usage. In terms of the current mobile and mobile VR games based on the Unity engine, the cost of memory is no more than the following three major parts:1. Resource memory consumption; 2. The engine module itself memory consumption; 3. Managed heap memory consumption.
If your project has a memory problem, you must not escape the above three scenarios. Today, we will explain each of these three cases one by one.
Resource Memory consumption
In a more complex medium-to-large project, the memory footprint of resources tends to occupy more than 70% of overall memory. Therefore, whether the use of resources is appropriate directly determines the memory consumption of the project. In general, the resources of a game project can be divided into the following categories: Texture (Texture), mesh, animation segment (animationclip), audio clip (audioclip), material (Material), shader (Shader), Fonts and text resources (text Asset), and so on. Textures, meshes, animated segments, and audio clips are among the most expensive resources to cause large memory overhead.
First, texture
Texture resources can be said to be a resource that occupies the maximum memory overhead in almost all game projects. A 60,000-patch scene, the largest grid resource is only 10MB, but a 2048x2048 texture, may directly reach 16MB. Therefore, the proper use of texture resources in a project can greatly affect the memory footprint of the project. So, what should texture resources be aware of when using them?
(1) Texture format
Texture formatting is a texture attribute that the development team needs to focus on most. Because it not only affects the memory footprint of the texture, but also determines the load efficiency of the texture. In general, we recommend that the development team choose the hardware-supported texture formats as much as possible depending on the type of hardware, such as etc on the Android platform, PVRTC on the iOS platform, DXT on the Windows pc, and so on. therefore, in the UWA Assessment Report, the texture format is listed in detail, so that the development team can quickly find, one-step positioning.
When you use a hardware-supported texture format, you may experience the following issues:
- Color scale problem due to etc, PVRTC and other formats are lossy compression, therefore, when the range of texture chromatic aberration span is large, it is inevitable to cause different degrees of "ladder"-like color scale problem. As a result, many research and development teams use the RGBA32/ARGB32 format to achieve better results. However, this practice will result in a large memory footprint. For example, the same 1024x1024 texture, if mipmap is not turned on, and is in PVRTC format, its memory consumption is 512KB, and if converted to RGBA32 bit, it is likely to occupy up to 4MB. Therefore, the development team in the use of RGBA32 or ARGB32 format texture, it must be carefully considered, the more sensible choice is to minimize the color range of the texture, so that it can use the hardware supported by the compression format for storage.
- ETC1 does not support transparent channel issues on the Android platform, for devices using OpenGL ES 2.0, the texture format can only support the ETC1 format, which has a serious problem, that is, the alpha transparent channel is not supported, so that the transparent map cannot be stored directly in the ETC1 format. In this respect, we recommend that the development team split the transparency map as much as possible into two pieces, that is, a RGB24-bit texture that records the color portion of the original texture and a transparent channel portion of a Alpha8 texture that records the original texture. The two posters are then transformed into ETC1-formatted textures and rendered with a specific shader to support transparent mapping. This method can not only approximate the rendering effect of RGBA transparent map, but also reduce the memory consumption of textures, which is a very recommended way to use.
Of course, there are more and more devices that support OpenGL ES 3.0, so you can further use ETC2 or even ASTC on the Android platform, which are all texture formats that support transparent channels and have a better compression ratio. If your game is suitable for people with middle-and high-end devices, then you can use these two formats directly as the primary storage format for textures.
(2) Texture size
In general, the larger the texture size, the greater the memory footprint. So, as much as possible to reduce the texture size, if the 512x512 texture for the display is sufficient, then do not use the 1024x1024 texture, because the latter memory consumption is four times times the former. therefore, in the UWA Assessment Report, we present the size of the textures in detail so that the development team can quickly detect them.
The mipmap is designed to effectively reduce the pressure on the rendering bandwidth and improve the rendering efficiency of the game. However, turning on mipmap will increase the texture memory by 1.33 times times. For 3D games with larger longitudinal depth, 3D scene models and characters we generally recommend that you turn on mipmap functionality, but in our assessment projects, we often find that some UI textures also turn on the mipmap feature. This is not necessary, the vast majority of the UI is rendered on the top of the screen, open mipmap does not improve rendering efficiency, but will increase the unnecessary memory consumption. Therefore, it is suggested that the research and development team should sort through mipmap in the UWA evaluation report, and examine whether the resources that turn on mipmap function are UI resources in detail.
(4) Read & Write
In general, the "Read & Write" feature of a texture resource is turned off by default in the Unity engine. However, we still find that the texture resources of many items turn on this option when the project is deeply optimized. In this respect, we recommend that the development team pay close attention to the use of this option in the texture resource, because turning on this option will increase the texture memory by one more time.
Second, grid
Grid resources tend to occupy higher memory in more complex games. For grid resources, what should it be aware of when using it?
(1) Normal, color and tangent
In many of our deeply optimized projects, the data for mesh resources often contain a large amount of color data, normal data, and tangent data. The presence of these data will greatly increase the file volume and memory footprint of the mesh resource. Where the color data and the normal data are mainly generated during the export of modeling software such as 3DMax, Maya, and tangent are typically generated when the engine is imported.
Even more troubling is that if the project makes a draw call batching operation on the mesh, it is likely to further increase the overall memory footprint. For example, 100 mesh is flattened, of which 99 mesh does not have color, tangent, and so on, and the other one contains the color, normal, and tangent attributes, then after the mesh is flattened, The three vertex attributes will be added to each mesh in Combinedmesh, resulting in significant memory overhead. That's why we've shown each mesh in the UWA Assessment report the exact use of its normal, color, and tangent attributes, and the development team can directly target each of these properties and directly locate the resources that have the redundant data.
In general, these data are mostly used by shader to produce cool effects. Therefore, it is recommended that the development team conduct detailed inspection of the grid resources in the project to see if the data is required for rendering in the rendering shader of the model.
Confined to space, we are only detailed on textures and grid resources today, and for animation segments, audio clips and other resources, we recommend that you check directly through the UWA Assessment report. At the same time, we will be in the follow-up resource topics to explain in detail, please look forward to.
The engine module itself occupies
The complexity of the memory overhead in the engine itself can be said to be accumulated by a huge amount of "tiny" memory, such as Gameobject and its various component (the largest component should be transform), Particlesystem, Monoscript and various modules manager (Scenemanager, Canvasmanager, Persistentmanager, etc.) ...
In general, the memory overhead of each component of the engine mentioned above is smaller, and the real memory overhead is those two:webstream and serializedfile. Most of its memory allocations are caused by assetbundle loading resources. Simply put, when you use new www or createfrommemory to load assetbundle, the unity engine loads the raw data into memory and extracts it, while the Webstream size is assetbundle original file size + The extracted data size + decompressionbuffer (0.5MB). Also, since the Assetbundle file before the Unity 5.3 version is LZMA compressed, its compression ratio is similar to zip (20%-25%), so for a 1MB original Assetbundle file, the size of the webstream after it is loaded may be 5~6MB , So when there are multiple Assetbundle files loaded through new www in the project, and Assetbundle cannot be released in time, the memory of Webstream is likely to be large, which the research and development team needs to keep an eye on.
Zhang Xin Link: https://zhuanlan.zhihu.com/p/21913770 Source: Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source.
For Serializedfile, the serialized file that is generated when you use the Loadfromcacheordownload, createfromfile, or new www local assetbundle file.
For Webstream and Serializedfile, you need to focus on the following two points:
- Whether there is a situation where assetbundle is not cleaned up. The development team can view its use-specific usage directly through the Unity Profiler and determine if the presence of assetbundle is justified when taking sample.
- For assetbundle files that occupy Webstream larger (such as the Assetbundle files related to UI Atlas), it is recommended that you replace them with loadfromcacheordownload or createfromfile. The extracted assetbundle data is stored in the local cache for use. This is ideal for projects with very tight memory, that is, by local disk space in exchange for memory space.
Note: For a detailed management mechanism for assetbundle, it is recommended to review our previous Assetbundle technical articles.
managed heap Memory consumption
The managed heap memory is allocated and managed by mono for most current projects based on Unity engine development. The idea of "hosting" is that mono can automatically change the heap size to fit the memory you need, and call the garbage collection (garbage Collection) operation to free up memory that is already unnecessary, thereby reducing the developer's threshold for code memory management.
However, this does not mean that the development team can open up the managed heap memory in code, because there is a serious problem with the mono version that Unity uses today, that is, once the heap memory of mono is allocated, it is not returned to the system. This means that mono's heap memory is only up and down. For example, when a project is running, it opens up 60MB of managed heap memory in scenario A, and when you go to the next scene B, only 20MB of managed heap memory is needed, then there will be 40MB of free heap memory in mono and will not be returned to the system. This is a phenomenon that we are very reluctant to see, because for games (especially mobile games), the memory footprint is the size of the gold, so that mono has no need to lock up a lot of memory, is a very wasteful thing. therefore, in the UWA Assessment report, we have counted the accumulated function heap memory allocations in the testing process for the research and development team, so that we can quickly view the underlying code implementation by looking at the function of the heap memory allocation TOP10, and locate whether there is any code that allocates unnecessary heap memory.
Zhang Xin Link: https://zhuanlan.zhihu.com/p/21913770 Source: Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source.
Reading this, you may have a question: I know which functions have large heap memory allocations, but how do I go about locating unnecessary heap memory?
This is a problem that we often encounter, so in our in-depth project optimization service, we will go directly to the project team, view the project code on site and locate the problem code. After a lot of in-depth detection, we found that the user's unnecessary heap memory allocation mainly comes from the following aspects:
- High frequency New Class/container/array and so on. Research and development team remember not to open up heap memory in the update, fixupdate, or higher call frequency functions, which can cause significant damage to your project's memory and performance. to do a simple calculation, suppose that a function in your project only allocates 100B of heap memory per frame, the frame rate is 1 seconds 30 frames, then 1 seconds the heap memory allocation of the game is 3kb,1 minutes of heap memory allocation is 180kb,10 minutes after the allocation of 1.8MB. If you have 10 such functions, then after 10 minutes, the allocation of heap memory is 18MB, during which time it may cause the spike in the heap memory of mono, and may cause multiple GC calls. In our evaluation program, a function is allocated hundreds of megabytes in 10 minutes, and sometimes even a gigabyte of heap memory is allocated.
- Log output. We found that in a large number of projects, there is still a large number of log output cases. It is recommended that the development team strictly control the output of its own log, preserving only critical logs to avoid unnecessary heap memory allocations. In this respect, the output of log is examined in detail in the UWA evaluation report, providing detailed performance overhead while occupying the call path of the log output. This allows the development team to locate and control the output of the log directly through the report.
Uipanel.lateupdate. This is the most expensive function of CPU and heap memory in Ngui. It is only a function in itself, but the use of Ngui makes it gradually become a rule that cannot be ignored. The function's heap memory allocation and its own CPU overhead are consistent at its root, which is caused by the rebuilding of the UI grid. Therefore, its corresponding optimization method is to directly view the CPU in the UI module explained.
Zhang Xin Link: https://zhuanlan.zhihu.com/p/21913770 Source: Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source.
There are a lot of points to note about the memory allocation of the code heap, such as string connections, the use of some engine APIs (getcomponent), and so on, which are commonplace, since space limitations are not covered here, and you are interested in Google's own search. Follow-up will also have specific code efficiency topics to explain, please pay attention.
UWA evaluation of memory standards
After you have used UWA, the UWA recommended memory standard value raised a lot of doubts. Here, we also share the rules for making the UWA memory standard.
(1) The overall memory standard of 150MB is mainly derived from the following two factors:
- After a lot of project optimization summed up. in fact, for the current market mainstream unity game, its memory consumption is mainly concentrated in 120~200MB. At the same time, taking into account the IPhone4 and 512MB/768MB and other low-end Android models, its application of the overall memory consumption can not exceed 200MB (iPhone4 security thread should be around 180MB), so we will reserved total set in 150MB, This is the unity engine's own memory allocation, to ensure that the app in the system library used, the overall memory of its OS is below 200MB.
- some channels have strict limitations on the PSS memory for Android games. General requirements of the game PSS within 200MB or less. This is another important reason why we set reserved total memory at 150MB.
(2) When the overall memory is set to 150MB, we further set the specific allocation. But it should be explained that the memory allocation here actually does not have the rigorous formula to carry on the argument, only is our experience value which extracts in the massive project optimization work. At present, the more reasonable memory allocation of the project is as follows:
- Texture resource: MB
- Grid resources: MB
- Animation segment: MB
- Audio clip: MB
- Mono Heap Memory: + MB
- Other: Ten MB
It should be noted that the 150MB does not cover more complex font files (such as Microsoft Black) and text Asset, which need to be based on the needs of the game.
(3) The current UWA memory standard is more stringent, for medium and high-end equipment, its content is actually much larger than 150MB. But we insist that a rigorous standard in the development process is a good thing for a project. At least, it can be a wake up for everyone to keep an eye on their own problems. According to our understanding, the current three to five line cities, the low-end mobile phone coverage is still quite high. At the same time, we are still experimenting and researching for mid-and high-end mobile devices. We hope that in the near future can be done for a variety of different grades of models are given a more reasonable recommended value, so that people more simple to manage the memory.
The above mentioned is the main memory allocation in the game project, you want to read here, you can better understand the memory overhead and potential problems of unity project, and the more targeted detection of their own projects.
There are three more important areas to focus on: memory leaks, mono invalid heap memory overhead, and resource redundancy. This is almost always a problem for all teams in the development process. Today, let's talk about the solutions to these problems in detail.
Zhang Xin Link: https://zhuanlan.zhihu.com/p/21913837 Source: Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source.
Memory Leaks
Memory leaks are the most common and least-likely problems that developers will encounter during the development of a project. For the time being, there are still some misconceptions about whether a project has a memory leak:
- Misunderstanding one my project in and out of the scene before and after the memory fall inconsistent, such as entering the scene, the memory increased by 40MB, after the drop of 30MB, still have 10MB memory does not return to the system, that is, the memory leakage situation.
- Misunderstanding two my project in and out of the scene, the Unity Profiler memory back to normal, but the PSS value of Android did not come down completely (after the scene PSS value is higher than before the scene of PSS value), that is, the memory leaks.
These are typical questions that our development team has come up with to give us feedback. It is believed that most development teams will encounter similar situations. It is important to note here that neither of these cases indicates that there is a leakage problem in memory. even if the memory continues to grow over time, it is not easy to determine that it has a memory leak. Because the memory can not be completely down the situation there are many, such as the memory of the resources after the storage for subsequent use, mono heap memory only up and so on, these can cause memory could not completely fall back . In general, the recommended way to determine if memory leaks is as follows:
First, check the use of resources, especially the use of textures, grids and other resources
In our project depth optimization process, resource leaks are a major manifestation of memory leaks, specifically because users store the loaded resources (for example, in container), but do not remove or clear them when the scene is switched. Thus, neither the engine itself nor the related APIs, such as manual calls to Resources.unloadunusedassets, can be uninstalled, resulting in resource leaks. Troubleshooting This situation is difficult because the amount of resources in the project is too large and leaking resources are often difficult to locate. As a result, every resource in the project is monitored in detail in the UWA Assessment Report, and the "life cycle" metric gives you a clear picture of the scope of each resource's use in the course of the project's operation.
This allows you to quickly see what resources are "resident" memory through the resource's life cycle property, and determine whether the resource is a preload or leak resource.
At the same time, the total number of resources used in the project is often hundreds of thousands, so it is a very laborious task for everyone to check the resources on a per-resource basis. Therefore, we have introduced the "scene comparison" function of resources. We recommend that you compare resources in the following two ways to quickly find resources that have a "leak" problem :
- Compare the same type of scene or the same scene
In general, the use of resources in the same scenario or the same scene should be more fixed, such as the main city scene or the main interface scene in a game project. By comparing the resource information of the same scene at different times, you can quickly find out the differences in their resource usage. In this way, you only need to determine whether the existence of these "difference" resources is reasonable, you can quickly determine whether there is a resource leakage, has been specific disclosure of resources.
- Comparison of different types of scenes
In addition to some resident resources, different types of scenarios, their resource use is completely different. For example, in the game of the main city and the resources of the battle copy, in addition to a small number of resident memory resources, the majority of resources used should be inconsistent. So, by comparing two different types of scenarios, you can directly view the "common resources" in the comparison results and determine whether they are really pre-defined resident resources. If it is not, it is likely to be a "leaking" resource that requires you to further see if there is a vulnerability to resource management for your project.
Second, through the profiler to detect the use of Webstream or Serializedfile
Improper management of Assetbundle also results in a memory leak, in which the assetbundle used in the previous scenario was not unloaded while the scene was switched, and was brought into the next scene. In this case, it is recommended to test it directly through take sample in the profiler memory, and to determine if there is a "leak" situation by directly looking at the Assetbundle name in Webstream or Serializedfile.
Third, through the Android Pss/ios instrument feedback app thread memory to view
To undertake the "misunderstanding of the second" in the said, "Unity Profiler memory back to normal, but the PSS value of Android did not come down completely" is possible, this is because the Unity Profiler feedback is the engine of the real allocation of physical memory, The part of the system's cache is recorded in PSS. In general, Android or iOS will not be in time to clean up all the app uninstall data, in order to ensure the next use of fluency, the OS will put some of the data into the cache, when its own memory is low, the OS Kernel will start a mechanism like Lowmemorykiller to query the cache and even kill some processes to free up memory. Therefore, it is not possible to indicate a memory leak by not completely dropping the one or two-time PSS memory.
Our recommended test method is to switch back and forth between two scenes, such as the main city and the Battle dungeon. Theoretically, switching the same scene multiple times, if the unity memory shown in the profiler falls back to normal, then its pss/instrument memory value fluctuation range also tends to stabilize, but if there is pss/instrument memory growth in the case, We need attention. This may be possible in two ways:
A memory leak problem with the unity engine itself . This is a small probability, previously only in a few versions.
A memory leak occurs when a third-party plug-in is in use . This is a large probability because the profiler can only monitor unity's own memory and cannot detect memory allocations from third-party libraries. Therefore, in the case of these memory problems, we recommend that you first use the third-party library to troubleshoot.
Invalid mono heap memory overhead
Currently, there is a big problem with the mono version that unity uses, i.e. once the memory is allocated, it is no longer returned to the system. This generates another problem- invalid mono heap memory . It is the heap memory allocated by mono, but it is not actually used, so it is called "invalid". So, how do I see if there is a larger number of "Invalid heap memory" In my project ?
In the UWA Assessment Report, we provide the allocation of memory as it runs with the project, as shown in. Among them, the separation of the Blue Line and the Purple Line reflects the allocation size of the invalid heap memory. For example, at the time selected in the graph, the Blue line reserved Total is the overall physical memory occupied by the current project, while the used total of the Purple Line is the overall physical memory used by the current project, indicating that the free memory in the current project is 57.1MB (200.4-143.3) , which consists mainly of two parts, the idle Unity Engine memory and the invalid mono heap memory. Where the free Unity memory is 17.1MB (92.0-74.9), the invalid mono heap memory for the currently selected frame is 40.0MB. Also, it can be seen that the blue Line and the Purple Line in the running process has been more open, indicating that there has been a small mono heap memory in an "invalid" state. This is a waste of things, especially for the memory of the size of the mobile device of gold.
So how do we avoid or reduce the allocation of too much "invalid heap memory" ? We recommend the following practices:
avoid large allocations of disposable heap memory . Mono's heap memory is also "on-demand" progressively allocated. However, if you open up a large heap of memory, such as new a larger container, loading a large configuration file, and so on, will inevitably cause mono's heap memory directly, so the research and development team on the allocation of heap memory need to pay attention to;
avoid unnecessary heap memory overhead . The UWA Assessment report lists the heap memory allocation TOP10 function during the project run, which is limited to space, and we no longer have one by one to repeat here, and the research team can directly view the previous article on memory optimization.
Resource Redundancy
In memory management, there is a topic that everyone must pay attention to--resource redundancy. In the large number of projects we have evaluated, more than 95% of the projects have varying degrees of resource redundancy. The so-called "resource redundancy" refers to the existence of two or more copies of the same resources in memory at a given moment. There are two main reasons for this to happen:
One, assetbundle packaging mechanism problems
The same resource was scored in multiple Assetbundle files. For example, the same texture is used by different NPCs, and each NPC is made into a separate assetbundle file, and the texture appears in different NPC Assetbundle files without relying on packaging for textures. When these assetbundle are loaded into memory, there is a case of texture resource redundancy in memory. In this connection, we suggest that the development team should check the production process of the related assetbundle after discovering the problem of resource redundancy.
At the same time, we have introduced a measure of "quantitative peaks" for each resource in the UWA assessment. It refers to the maximum number of occurrences of the same resource in the same frame. If it is greater than 1, it is likely that there is a "redundant resource" for that resource. You can sort by this column to see the redundancy of resources in your project immediately.
Ii. due to the instantiation of resources
In the unity engine, when we modify the resource properties of some specific gameobject, the engine automatically instantiates a resource for that gameobject for its use, such as material, mesh, and so on. In material, for example, we often do this in research and development: When a character is attacked, it changes its material properties to a specific hit effect. This approach causes the engine to re-instantiate a material for a particular gameobject, with the suffix appended (instance). There is no particular problem in itself, but when there are more gameobject to change the material attribute requirements (such as ARPG, MMORPG, Moba and other game types), the amount of redundancy in memory increases. As shown, the instantiated material resources increase to 333 as the game progresses. Although the memory footprint of material is small, too much redundant resources add considerable pressure to the efficiency of Resources.unloadunusedassets API calls.
In general, the change of resource attributes is fixed, not random. For example, assuming that the gameobject is under attack, its material attribute changes with three different parameter settings depending on the type of attack. So, for this kind of demand, we recommend that you directly make three different material, in the runtime with the code to replace the corresponding gameobject material, rather than change its material properties. In this way, you will find that hundreds of thousands of instance material have disappeared in memory and replaced by these three different material resources. The benefits, for you to be able to read here, should have no need for me to say more. :)
The above is our experience in memory optimization work, and hope that it will be helpful to your project development. Optimization never unified standard scheme, only the most suitable for your project, I hope you can ingenious, do not let go of any place that makes you feel "wrong". Finally remind everyone--" do not take good small and not for, do not use evil small and for it ", Mutual encouragement!
Reprinted from: https://zhuanlan.zhihu.com/p/21913837 Source: Know
Memory optimization for Unity3d game development