I will have a script which will exist on thousands/millions of GameObjects. For obvious reasons it will start taxing player’s computer. I have a couple theories to test in order to improve performance of massive amounts of the same script. However I don’t know a way to measure it. What would be a way to directly and deterministically measure the amount of computation required for processing, a % in Resource Manager won’t make it, and I don’t know how reliant is the Profiler. Is there also a way to keep track of memory usage, and the function call delay? The script will contain async and IEnumerators and I would like to include those into the test as well.
The important measurement factors to me are execution time, execution delay (if I create another instance, how long will it take to start a new script whilst the others are already taxing), average memory used (so I can shove off any megabytes needed), average processing tax (to see if I improved performance of my script).
I’m looking for something reliable that will drop a specific number, I don’t consider eye, guessing and estimating of good value. I’ll repeat my tests a couple times.
As a side question, is it possible to let GPU leverage some of the processing from the CPU? Reverse GPU instancing?
If it’s just a data container, it won’t be that taxing (beyond the memory implications which are inherent to have millions of anything).
But if say the script utilizes the ‘Update’ method, that means ‘Update’ is being called on all million objects every frame. This can be taxing.
You should open the profiler (Window->Analysis->Profiler) and profile it. If you go into the ‘hierarchy’ view of it you can locate your scripts Update (or other methods) and see the cost of it relative to the total game cost:
Here’s an example of my script ‘CrosshairUI’ and its ‘LateUpdate’. It doesn’t take up very much of my resources.
More documentation on the profiler:
Note… if you’re trying to update millions of objects. This is usually where things like ECS comes in and allows for more efficient updates of that many objects.
But like I said… if it’s not doing stuff every update, the script isn’t going to be that costly.
This isn’t to say other aspects may be… such as the rendering of those million objects. Things like batching help there. But without more information about what it is you’re doing… I’m only speculating at this point.
I’ll just say, if you’re even considering the possibility of millions of objects and aren’t using ECS, you absolutely need to do that right now. No matter what other factors might be in play, you will never get acceptable performance for millions of Unity objects without using ECS.
ECS will have things running in loops, and you can put your Profiler calls outside of those loops, which will give you more accurate overall performance numbers less dependent on Profiler’s own overhead.
Ooof, but that’s the problem. Script will perform Update() processing, this is why it will be taxing.
ECS? None of the result yield positive result, the first is Amazon ECS. There are other definitions but programming unrelated.
Also, the profiler you mentioned. I have some scripts which do use Update, but I can’t seem to find them in the ocean of Hierarchy and Raw Hierarchy. I do see other Updates, but none of the dropdowns contain mention of my script. Even when I type their full name in search bar. In your case it would be “CrosshairUI”.
Also, is there a way to hone it down to specific class? My solutions are based around multiple functions, async functions and IEnumertors and it will be an easy mistake for me to add them up incorrectly without knowing it. Profiler seems exactly like the tool that does exactly what I need, but I’d love it to single out specific class (and all its instances), can it be done? After minor examination I discovered that threads are segregated, that would be a solution, but then I found out that Unity doesn’t support external separate threads.
Edit: I see ECS, Entity Component System, but what are its drawbacks?
@StarManta gave you a link already. You’re going to want to use it.
You used the search bar and searched for the name of your script? It should come up. You have it set to hierarchy right?
You should definitely read through the documentation of the profiler. Get familiar with it.
Definitely read the documentation. I’m not the go to guy for specifics about it.
Drawbacks are subjective. It’s a different paradigm of looking at/solving your problems. It’s geared towards managing large numbers of objects asynchronously. You should check it out… it’s biggest benefits come in when managing millions of objects that need to be updated.
Pros: Insanely efficient.
Cons: Harder to learn how to use, less pre-existing scripts available.
If your situation is as described, though, there’s honestly no point in discussing drawbacks. Running a million objects in Unity at any kind of acceptable framerate just flat out is not possible without using ECS (or implementing something on your own that would essentially be ECS).
Do yourself a favor and run this experiment, to create the most basic theoretical possible “million objects running Update()” setup:
Create a script with nothing but an empty Update() method:
public class EmptyUpdate : MonoBehaviour {
void Update() { }
}
Create a second script which instantiates the above:
public int count = 1000000;
void Start() {
for (int i=0;i<count;i++) {
GameObject go = new GameObject($"Object {i}");
go.AddComponent<EmptyUpdate>();
}
}
Run it
Now, when I did this on my desktop (which is no slouch performance-wise) and ran it, it took almost a minute to execute Start() and then I briefly got about 3 frames over the next 5-10 seconds and then Unity crashed. That’s with the script doing literally nothing.
Anyway, the point of this is: downside or no downside, learn about ECS. It is the only possible way this can ever work.
It sounds curious to me that you didn’t find your scripts in there. If the code executes in Update and an instance of that script existed in the frame you’re looking at, it should show up under it’s name when searching for it. (Everything else would be a bug)
You could also wrap the code about whichs performance you are concerned in ProfilerMarker Begin/End calls to give it a name that would stand out more and is maybe similar across all the code related to the script, even if it isn’t part of that class.
To analyze the performance more in depth and in breadth than one frame at a time, you should also look into the Profile Analyzer. With that, you can e.g. run (A|B) experiments over multiple frames and then compare them too each other.
I’m planning to undertake mission of translating my rather simple game from MonoBehaviour to ECS. However I am currently ignorant of underlying mechanics which leaves me with remaining question. Lets say I would make 100000 NPCs in ECS system, but I would leave camera management, networking and audio management in MonoBehaviour. Is this something that can be done?
The largest problem for me is that I use a known networking library called Mirror. That library leverages a lot of networking jobs, however its written with classical MonoBehaviour technique. I asked almost same question there, however developers told me that they know little about ECS, so they couldn’t help me.
All that in a single question: If I create the most processing intensive scripts with ECS method (like AI for hundreds of thousands of soldiers) and keep my camera, sound and Mirror as MonoBehaviour, is that expected to work properly and as expected? This way I could keep the familiarity of MonoBehaviour’s simpletonness on non-intensive scripts and selectively choose which intensive parts could be optimized with ECS.
Just want to make a rather obvious point, that ECS is a far more efficient way of managing large numbers of objects, but it isn’t magic. Your game is still limited by the resources of the hardware the game is running on. The AI for hundreds of thousands of units will still need to be designed extremely efficiently in order to work.
Also, since this is a network game using Mirror, I certainly hope you don’t have the crazy idea of just throwing a NetworkTransform on each of these hundreds of thousands of units and expecting that to work. It will certainly not. You’ll have to design your own system for efficiently syncing that number of units. This might be a job for a lockstep approach instead of sending position updates.