Monitoring and optimization of the Unity coprocessor runtime

Source: Internet
Author: User

I am the happy porter: Http://gulu-dev.com/post/perf_assist/2016-12-20-unity-coroutine-optimizing#toc_0

------------------------------------------------------------------------Split Line------------------------------------ ----------------------------------------

Directory:

Warm up: Speaking from the reuse Yield object
How the Coroutine works
Take over and monitor the behavior of coroutine? Problem description
Middle Trackedcoroutine
Start function Invokestart ()
Monitoring the Plugins within the

Perfassist Components-Coroutinetracker (on GitHub)? function Introduction
FAQ Survey

The coroutine is a very useful mechanism that is provided by most modern programming environments. It allows us to combine behaviors that occur at different times in a linear way in the code. Compared with the system based on event and callback, the business logic is organized in a way that is relatively readable.

In Unity, the implementation of the process is the simplification of the traditional process-in the main thread at each frame at a given point in time, the engine through a certain scheduling mechanism to wake up and execute the conditions of the implementation of the process, with the actual time-sharing serialization to avoid the communication between the process of the problem. However, due to various factors, the implementation of the process is relatively less transparent to programmers, you can use some simple mechanism to monitor and optimize it.

Warm up: Speaking from the reuse Yield object

Let's start with one of the simplest and most straightforward improvements. The following is an example of a process that executes at the end of each frame:

void Start () {    startcoroutine (Onendofframe ());} IEnumerator Onendofframe () {    yield return null;    while (true)    {        //debug.logformat ("called on Endofframe.");        Yield return new Waitforendofframe ();}    }

As you can see in the Profiler, the above code causes WaitForEndOfFrame each frame of the object to be allocated and adds a burden to the GC. Assuming that the game has 10 active threads running at FPS, the GC incremental burden per second is 10 60 16 = 9.6 KB/s .

We can easily optimize this overhead by reusing a global WaitForEndOfFrame object:

static Waitforendofframe _endofframe = new Waitforendofframe ();

After creating a global share in the right place _endOfFrame , simply change the code above to:

    ...    Yield return _endofframe;    ...

The GC overhead of the 9.6 kb/s above is completely avoided, and there is absolutely no difference between logic and optimization.

In fact, all inherited YieldInstruction instruction types used to suspend the process can use the global cache to avoid unnecessary GC burdens. Common ones are:

    ? Waitforseconds     ? Waitforfixedupdate     

In Yielders.cs This file, the static objects of the above types are created centrally, which can be used directly:

    ...    Yield return yielders.getwaitforseconds (1.0f);  Wait for one second ...    

How the Coroutine works

Observing the call chain, the Unity Coroutine's calling convention is maintained by the returned IEnumerator object. IEnumeratorthe core function functions we know are:

BOOL MoveNext ();

This function is called every time that the Unity co-scheduling function (usually the setupcoroutine ()) of the class in which the coprocessor is invoked, is used to drive the corresponding process from the last yield statement to execute the following code snippet until the next yield statement (corresponding to return true) or the function exits (the corresponding return is false).

is a typical co-invocation:

The green solid block in the figure is the actual active execution time of the process. As you can see, the full life cycle of a process is "a traversal of all code snippets within the entire life cycle and executed sequentially" .

Take over and monitor the behavior of Coroutine

Problem description

Due to the existence of the following problems, the implementation of the process is not transparent to developers, it is easy to introduce performance issues during the development process.

    1. The co-process (except for the first execution) is not triggered within the user's function, but SetupCoroutine() is activated and executed within a separate
    2. Each active execution of the process is bounded by a single yield on the code. It is difficult to see the potential execution of each yield for a business logic with a complex branch, especially a code that is "inherently in the main flow and later being co-structured".
    3. In practice, if the simultaneous activation of the process is more, there may be multiple high-overhead co-extrusion in the same frame to execute the resulting card frame. This type of lag is difficult to reproduce and investigate.

InterlayerTrackedCoroutine

For these cases, we can add a layer of Wrapper between the main process and the coprocessor to take over and monitor the execution of the actual process. Specifically, you can implement a purely forwarded IEnumerator, as shown in the following reduced version:

public class trackedcoroutine:ienumerator{    IEnumerator _routine;    Public Trackedcoroutine (IEnumerator routine)    {        _routine = routine;                In this case, the Hkja of the path is created    }    object IEnumerator.Current    {        get        {            return _routine. Current;        }    }    public bool MoveNext ()    {        ///Here you can:        //     1. The execution of the standard Hkja        //     2. Record the time of the execution of the schedule        bool Next = _ Routine. MoveNext ();        if (next)        {            //a normal execution        }        else        {            //The coprocessor runs to the end, has ended        }        return next;    }    public void Reset ()    {        _routine. Reset ();}    }

The full version of the code is shown in the implementation of the Trackedcoroutine class.

With this one TrackedCoroutine , we can put the normal

Abc. Startcoroutine (XXX ());

Replaced by

Abc. Startcoroutine (New Trackedcoroutine (XXX ()));

Start functionInvokeStart()

In the Runtimecoroutinetracker class, you can see the following two interfaces, for a IEnumerator , string , and optional single-Parameter form, three forms of the coprocessor-initiated package.

public class runtimecoroutinetracker{public    static Coroutine Invokestart (Monobehaviour initiator, IEnumerator routine);    public static Coroutine Invokestart (Monobehaviour Initiator, String methodName, object arg = null);}

The above external call can be replaced by:

Runtimecoroutinetracker.invokestart (ABC, XXX ());

At this point, with an intermediate layer TrackedCoroutine , we are able to take over and monitor a single run of all the co-processes.

Monitoring the Plugins within the

Since the Plugins directory is compiled separately and cannot be directly called externally, we provide a forwarding mechanism for all plug-ins to forward the request to the startup function in the plugin.

First, define two delegates:

Public delegate Coroutine Coroutinestarthandler_ienumerator (Monobehaviour initiator, IEnumerator routine);p ublic Delegate Coroutine coroutinestarthandler_string (monobehaviour initiator, String methodName, object arg = null);

The actual process request is then forwarded to the two delegates:

public class coroutinepluginforwarder{...    public static Coroutine Invokestart (Monobehaviour initiator, IEnumerator routine)    {        return Invokestart_ IEnumerator (initiator, routine);    }    public static Coroutine Invokestart (Monobehaviour Initiator, String methodName, object arg = null)    {        return Invok Estart_string (initiator, methodName, ARG);    }    ...}

Finally, you can register two delegates at run time:

Coroutinepluginforwarder.invokestart_ienumerator = Runtimecoroutinetracker.invokestart; coroutinepluginforwarder.invokestart_string = Runtimecoroutinetracker.invokestart;

The complete code implementation is shown in the Coroutinepluginforwarder class.

Perfassist Components-Coroutinetracker (onGitHub)

On the basis of these implementations, I implemented a tool panel Coroutinetracker in the editor to help developers Monitor and analyze the operation of the system in the process.

Https://github.com/PerfAssist/PA_CoroutineTracker

function Introduction

The four columns on the left are the real-time startup times, end times, number of executions and execution times of all the tracked processes when the program is running.

When you click anywhere on the graph, select the point in seconds, which is the green vertical bar on the graph.

At this point the data report on the right is refreshed as a list of all the threads that are active in this second, as shown in:

Note that the data in this table is in turn:

    • Full decorated name of the association (mangled name)
    • Number of executions within the selected time period (selected execution count)
    • Execution time within the selected time period (selected execution times)
    • The total number of executions (summed execution count) until the selected time
    • The total execution time (summed execution time) until this is selected

The data for each column can be sorted by the header.

When one of the threads in the list is selected, the details of the process are displayed in the lower-right corner of the panel, as shown in:

Here are the following information:

    • The sequence ID (sequence ID) of the process
    • Start Time (creation)
    • End time (termination)
    • Boot-time stack (creation StackTrace)

Scroll down to see the full execution flow information for the process, as shown in:

FAQ Survey

Using this tool, we can more easily investigate the following questions:

    • Yield is too frequent
    • One-time operation for too long
    • The total time overhead is too high
    • Into the dead loop, which never ends properly.
    • Recursive yield yields a deep level of execution

Monitoring and optimization of the Unity coprocessor runtime

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.