PhotonHandler

Hi. Why does it happening? http://clip2net.com/s/1eYFZ

Comments

  • good call
  • Looks like GC.Collect is called pretty often, do you call it manually ?
  • can you please attach the photonhandler script you use?
  • very odd.. the picture shows CPU peaks caused by the Garbage Collector and it also shows that PhotonHandler.Update() calls GC.Collect, but I cannot find any GC.Collect calls in any script or library of ours...
  • Maybe it is automatically called by unity in any c# code when needed?
  • it happens automatically in .net when needed. Unity does not use it as unity objects are not .net objects, they are C++ objects with binders.

    commonly a good chance to cause spikes is usage of tag - strings in general. they are garbage bombs normally when not used correctly
  • that's right dreamora, but I did not expect to see a GC.Collect() call in the profiler.
    Automatic garbage collection every few seconds is also very unusual, unless your process is running out of memory.
  • Boris wrote:
    that's right dreamora, but I did not expect to see a GC.Collect() call in the profiler.
    Automatic garbage collection every few seconds is also very unusual, unless your process is running out of memory.

    in MS.NET yeah
    in Mono 2.6 in Unity it happens every few seconds under heavy trashing caused by two possible roots out of my experience so far: strings and string ops which generate plenty new strings that get insta trashed and the master trash bomb known as OnGUI thats able to kill any cpu and GC ...
  • ok, for mono I don't know, you are probably right.
    creating strings for no reason has a huge impact on performance under .net as well (for example debug logging without checking whether debug is enabled).
  • Its not 'no reason' actually

    strings are reference types.
    every string operation generates a new temp string for the operation result
    with large concatenated string operations you can easily create half a dozen dummy objects. Put that in a function called 100 times a second and you run on stuttery 5 fps on mobiles and even webplayer will go down seriously. at worst people try to use really incode strings which are by definition temps and get garbaged right away, leading to even more trash. (favorite beginner error: Never heard of ToString() and uses "" + xxxx to get int to string. thats a great way to create a good pile of trash)

    if you want to make it even worse write death code and do string ops in ongui without an event checking and you get thousands of garbage objects a second without the smallest problem ;)
  • http://clip2net.com/s/1fE2m
    http://clip2net.com/s/1fErn - Deep Profile

    What should I do? I do not use OnGUI.

    //NetworkPeer - public void ExecuteRPC(Hashtable rpcData, PhotonPlayer sender) -
    foreach (UnityEngine.MonoBehaviour monob in photonNetview.GetComponents<UnityEngine.MonoBehaviour>())
            {
                Type type = monob.GetType();
                //Get [RPC] methods from cache
                List<MethodInfo> cachedRPCMethods = null;
                if (monoRPCMethodsCache.ContainsKey(type)) cachedRPCMethods = monoRPCMethodsCache[type];
                if (cachedRPCMethods == null)
                {
                    List<MethodInfo> entries = new List<MethodInfo>();
                    MethodInfo[] myMethods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                    for (int i = 0; i < myMethods.Length; i++)
                    {
                        if (myMethods[i].IsDefined(typeof(UnityEngine.RPC), false))
                        {
                            entries.Add(myMethods[i]);
                        }
                    }
                    cachedRPCMethods = monoRPCMethodsCache[type] = entries;
                }
                if (cachedRPCMethods == null)
                    continue;
                //Check cache for valid methodname+arguments
                foreach (MethodInfo mInfo in cachedRPCMethods)
                {
                    if (mInfo.Name == methodName)
                    {
                        foundMethods++;
                        ParameterInfo[] pArray = mInfo.GetParameters();
                        if (pArray.Length == argTypes.Length)
                        {   //Normal, PhotonNetworkMessage left out
                            if (CheckTypeMatch(pArray, argTypes))
                            {
                                receivers++;
                                object result = mInfo.Invoke((object)monob, deParams);
                                if (mInfo.ReturnType == typeof(System.Collections.IEnumerator))
                                    PhotonHandler.SP.StartCoroutine((IEnumerator)result);
                            }
                        }
                        else if ((pArray.Length - 1) == argTypes.Length)
                        {   //Check for PhotonNetworkMessage being the last
                            if (CheckTypeMatch(pArray, argTypes))
                            {
                                if (pArray[pArray.Length - 1].ParameterType == typeof(PhotonMessageInfo))
                                {
                                    receivers++;
                                    object result = mInfo.Invoke((object)monob, deParamsWithInfo);
                                    if (mInfo.ReturnType == typeof(System.Collections.IEnumerator))
                                        PhotonHandler.SP.StartCoroutine((IEnumerator)result);
                                }
                            }
                        }
                    }
                }
            }
    
  • Would have to profile it in detail.
    But without further profiling especially the cachedRPCMethods list seems to be a major trash generator as it gets regenerated every frame (its no static and declared in loop instead of out loop. unsure mono does the method globalization like .net4 does)
  • I looked into this today and played with the profiler. The issue you raise is relatively rare and fairly random. I paused several times to look into the methods called and got stuck at different places. Sometimes a lot of time is spent on SendOutgoingCommands, sometimes it's ExecuteRPC. In more than one case, the profiler identified the Stack Trace code of Debug.Log to take 10ms or more.
    In worst case, the which kicks in at some random place and the profiler will include this delay in a method's time-slice. It's unclear how this works in detail. Unity's page about profiling is not exactly detailed.

    I talked to Mike about the profiler's reliability and what exactly it will account into methods. From his perspective, it's not a good tool right now. DeepProfile has a lot of overhead and affects the results. In his opinion, we should delay all profiling until Unity 3.5 comes out, which should have a better profiler.


    Performance was not the goal of Photon Unity Networking so far. Having a more or less stable version out now, we can get into the details now and will try to avoid object-trash and other resource issues.
  • Boris wrote:
    that's right dreamora, but I did not expect to see a GC.Collect() call in the profiler.
    Automatic garbage collection every few seconds is also very unusual, unless your process is running out of memory.

    In MS .NET and in 'we don't give a crap until we run out of memory' GC environments thats true
    UT unhappily (or happily otherwise it wouldn't run on iOS at all thanks to apples RAM strategy) opted their Mono for mobile a bit, in consequence it goes out much more agressive on cleaning. I'm unsure how agressive it is in detail ie how many generations etc it eats but what I know is that I've not seen it getting beyond 15s more or less without GC.collect since unity 2.5 and Windows support anymore. how bad that is depends on trash piling.

    my iOS titles pretty normally have a manual gc collect every 1-3s to prevent the piling cause 0-1ms every second does not impact the smooth experience but a 10ms+ lock every 10-15s is deadly


    In this case here it won't help though, with such a massive piling within that time you would have to call gc.collect on a rate where the overhead will no longer pay out favorable by splitting the collects


    I agree with Mike (whoever that might be) though: Unitys deep profiler is NOTHING you want to use to learn something about the performance profiling reliably. it trashes the whole environment that badly that it gets hard to use. at worst you use it on a play mode application then you are just doomed, never use it anywhere but a standalone!
    if you care about it, try to profile it on osx using shark + instruments with a development build. (ms tools potentially allow that too but I don't grow money trees and the realistic developer oriented VS subscriptions are just unpayable and unreasonable given XCode gives it for free ... MS definitely has to go over their books there for VS 2012)