GC Allocation

Post here your questions about the Unity / .Net / Mono / Windows 8 / Windows Phone 8 API for SFS2X

Moderators: Lapo, Bax

Mrm83
Posts: 121
Joined: 17 Dec 2017, 04:02

GC Allocation

Postby Mrm83 » 13 Jun 2018, 17:48

I am syncing my player's position via UserVarableRequest when they move, i don't do it every frame, only when the min distance threshold is reached, roughly 5 times a second if they move in a straight line.
Each move requires creating a SFSUserVariabler for x,y,z. Is there a way to reuse the same object instead of creating a new one every send request? This is creating a lot of GC. (MOBILE)

Code:
userVariables.Add(new SFSUserVariable("x", (double)pos.x));
userVariables.Add(new SFSUserVariable("y", (double)pos.y));
userVariables.Add(new SFSUserVariable("z", (double)floor));

The above alone creates 20kb gc every sec when my player moves.

I thought about using SFSObject and send it as a custom requesthandler as I can reuse the object, but when I set the uservariables from server side, it doesn't notify the clients so that option won't work.
User avatar
Lapo
Site Admin
Posts: 22473
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: GC Allocation

Postby Lapo » 14 Jun 2018, 07:43

Hi,
we've discussed this in an email with another client (or maybe it was you?)

We are aware of this but we're also aware that even a simple call to Debug.Log() causes similar spike in the GC graph. Memory has to be allocated when data goes out, and C# has no control over the actual RAM being used, meaning that you don't get to decide how much memory is allocated when you use the "new" keyword to instantiate objects.

We've already tested this multiple times and notice that even the simples of operations in Mono/Unity causes similar activity in the GC so I think this is pretty normal for that environment.

Code: Select all

userVariables.Add(new SFSUserVariable("x", (double)pos.x));

You can keep a reference to the old user variable and reuse it. Like this:

Code: Select all

var myUVar = new SFSUserVariable("x", (double)pos.x);

and then reuse that variable instead of using "new" every time. It should work.

Hope it helps
Lapo
--
gotoAndPlay()
...addicted to flash games
Mrm83
Posts: 121
Joined: 17 Dec 2017, 04:02

Re: GC Allocation

Postby Mrm83 » 14 Jun 2018, 11:12

No it was not me.

Unfortunately, your suggestion does not work (unless I am not understanding your idea correctly).
var myUVar = new SFSUserVariable("x", (double)pos.x);
and then reuse that variable instead of using "new" every time. It should work.

How do I reuse myUVar? I can't reassign the value of "x" as it is read only. Just sending myUVar after creating it doesn't work as it doesn't seem to reference the updated value, only the value of x when myUVar was created. So in order to change the "x" value, I would still need to do "myUVar = new SFSUserVariable("x", (double)pos.x);" again and again which is pointless.

In C#, instantiating a class means gc that is why I reuse all objects (pooling) if possible to avoid having to instantiate a new object by calling "new".
We've already tested this multiple times and notice that even the simples of operations in Mono/Unity causes similar activity in the GC so I think this is pretty normal for that environment.

No, it is not normal. If we know creating a new object causes GC, we pool it and reuse it instead of creating it again and again. So not pooling or reusing is NOT normal (if the goal is to save memory).

In comparison.. my PUN production project does 0B of gc alloc every frame and gc alloc only happens once a while when new objects needs to be created.
vs
right now every 1 sec 10kb...
User avatar
Lapo
Site Admin
Posts: 22473
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: GC Allocation

Postby Lapo » 14 Jun 2018, 14:46

I stand corrected, you cannot overwrite the value of an existing SFSUserVariable on the client side, so yes you would need to create a new Object.

In C#, instantiating a class means gc that is why I reuse all objects (pooling) if possible to avoid having to instantiate a new object by calling "new".

In C# nobody does that (pooling), but in Unity it is done sometimes as on optimization, where possible.
Unity itself doesn't do it that much as you can see by yourself invoking most of the existing API.

We could add an extra method on UserVar and RoomVar to overwrite the existing value, and avoid creating new instances.

No, it is not normal. If we know creating a new object causes GC,

You realize that a program in order to do something has to use resources? If C# did not run a GC you would have to spend an equal amount of CPU to clean your memory anyways. The only difference is that the GC doesn't do it exactly when you want it, and therefore you have to be careful not to overload it.

Have you measured how much CPU % is used by the GC?
Lapo

--

gotoAndPlay()

...addicted to flash games
User avatar
Lapo
Site Admin
Posts: 22473
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: GC Allocation

Postby Lapo » 14 Jun 2018, 15:31

For reference an empty Unity project with no assets and no game objects on the scene, besides the camera, generates ~187 allocations and 7-10Kb of GC per frame, according to its own profiler.

However these numbers have very little meaning without some context, the context being how much this impacts the actual performance of the application running. Did you investigate this aspect? Can you give us some numbers and context? (e.g. what device has been used and type of test performed)

Thanks
Lapo

--

gotoAndPlay()

...addicted to flash games
User avatar
Lapo
Site Admin
Posts: 22473
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: GC Allocation

Postby Lapo » 14 Jun 2018, 15:58

One more thing, you can bypass entirely the UserVariables especially if you're sending tens of updates per second and instead just use an Extension request.

You can even recycle the same SFSObject if you wish, and maybe just send a single binary payload (byte[] wrapped in SFSObject) instead of structured data to avoid further steps of serialization.

Cheers
Lapo

--

gotoAndPlay()

...addicted to flash games
v.potapov
Posts: 18
Joined: 12 Feb 2020, 06:29

Re: GC Allocation

Postby v.potapov » 15 Jun 2022, 11:57

Hi @Lapo,

You've sad
We could add an extra method on UserVar and RoomVar to overwrite the existing value, and avoid creating new instances.


Is it possible to do such a thing? We've also struggling with allocations, which are produced by creating sfs requests. Every frame it is ~16kb. GC can handle it, but it will be experienced as a huge spike during runtime.

We are using 1.7.14.0 version,

Best Regards
User avatar
Lapo
Site Admin
Posts: 22473
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: GC Allocation

Postby Lapo » 16 Jun 2022, 09:35

Hi,
why is it every frame though? Are you updating User Variables every frame? At what FPS?

Thanks
Lapo

--

gotoAndPlay()

...addicted to flash games
v.potapov
Posts: 18
Joined: 12 Feb 2020, 06:29

Re: GC Allocation

Postby v.potapov » 20 Jun 2022, 14:49

Hi Lapo,
Packets handle logic happen near 30times per sec, regadless FPS. Anyway, the main reason to asking you about memory reuse is to support weak devices (Nintendo switch is a good example). Does it really necessary always creating new objects/arrays under the hood? Our title is being implemented on most of platforms, but we struggling with allocation problem only on a switch

Thanks
User avatar
Lapo
Site Admin
Posts: 22473
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: GC Allocation

Postby Lapo » 20 Jun 2022, 18:50

I see what you mean.
It would help to know a bit more about your use case and analysis of the object allocation. If you want to share these privately you can get in touch with us via our support@... email box, adding a reference to this discussion.

For instance, you mentioned you have ~30fps update cycle. What is the main way to send these updates? Extension messages? User Variables? Other? Mainly TCP? or UDP also?

Have you found specific objects to be particularly memory expensive for your use case?

Thanks
Lapo

--

gotoAndPlay()

...addicted to flash games
v.potapov
Posts: 18
Joined: 12 Feb 2020, 06:29

Re: GC Allocation

Postby v.potapov » 21 Jun 2022, 13:01

Hi Lapo,

The main way to send updates is via ExtensionRequests, every call allocating its new object.

Here is the list of the most memory consumption places
SmartFox.cs > Send()
BitSwarmClient.cs > Send()
SFSProtocolCodec.cs > OnPacketWrite()
SFSIOHandler.cs > OnDataWrite()
SFSObject.cs > OnBinary()
DefaultSFSDataSerializer.cs > Object2Binary() > Obj2Bin() > EndoceObject()

All these methods creates everytime new ByteArray(), especially heavy "Obj2Bin() > EndoceObject()" section.
Please tell if you still need to investigate via private support

Thanks
User avatar
Lapo
Site Admin
Posts: 22473
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: GC Allocation

Postby Lapo » 21 Jun 2022, 16:45

Hi,
I see what you mean, however those allocations are necessary. Both the network protocol codec and the object serializer work in onion-like fashion where every step passes the result of its work to the next stage.

However the stackalloc expression introduced in C# 7.2 might be of help. By allocating the byte[] used by the protocol codec on the stack each intermediary byte array would be removed from memory when the method returns. Thus avoiding GC cycles.

I think we'll experiment with this approach and if it works well we'll turn it into a release but I don't see this happening in the immediate future (e.g. month) as we're already hard at work on the cloud-based clustering for SFS2X.

If you want to experiment with this yourself earlier we can send the sources of the Unity API and you can take a stab at it. In such case get in touch with us directly via our usual support@... email box.

Thanks
Lapo

--

gotoAndPlay()

...addicted to flash games

Return to “SFS2X C# API”

Who is online

Users browsing this forum: No registered users and 11 guests