Search Unity

Help with the Job System implementation.

Discussion in 'C# Job System' started by chuakc92, Mar 14, 2018.

  1. chuakc92

    chuakc92

    Joined:
    Nov 22, 2016
    Posts:
    20
    So this is my code for moving a number (count) of spheres around an elliptical orbit. It works just like I want it to.
    Code (csharp):
    1.  
    2. public class Simulation : MonoBehaviour
    3. {
    4.    [Header("Sphere Generation Settings")]
    5.    public int count = 100;
    6.    public float radius = 5;
    7.  
    8.    [Header("Sphere Orbitting Settings")]
    9.    public float orbitSpeed = 3f;
    10.    public Vector2 orbitPath;
    11.  
    12.    GameObject[] spheres;
    13.    Vector2[] sphereOrbitPaths;
    14.  
    15.    float[] orbitProgress;
    16.    float[] orbitYAxes;
    17.  
    18.    private void Start()
    19.    {
    20.        // gets an array of spheres instantiated into the scene and placed at random radii
    21.        spheres = GetComponent<SphereGenerator>().PlaceSpheres(count, radius);
    22.  
    23.        sphereOrbitPaths = new Vector2[count];
    24.        orbitProgress = new float[count];
    25.  
    26.        for (int i = 0; i < sphereOrbitPaths.Length; i++)
    27.        {
    28.            //sets the orbital path x and y from the center
    29.            sphereOrbitPaths[i] = orbitPath;
    30.          
    31.            //randomly sets the travel progress from 0% to 100%
    32.            orbitProgress[i] = Random.Range(0f, 1f);
    33.        }
    34.    }
    35.  
    36.    private void Update()
    37.    {
    38.        for (int i = 0; i < spheres.Length; i++)
    39.        {
    40.            orbitProgress[i] += Time.deltaTime * orbitSpeed;
    41.            orbitProgress[i] %= 1f;
    42.          
    43.            //moves the object in a elliptical orbit
    44.            spheres[i].transform.position = Evaluate(sphereOrbitPaths[i].x, spheres[i].transform.position.y, sphereOrbitPaths[i].y, orbitProgress[i]);
    45.        }
    46.    }
    47.  
    48.    public Vector3 Evaluate(float xAxis, float yAxis, float zAxis, float t)
    49.    {
    50.        float angle = Mathf.Deg2Rad * 360f * t;
    51.        float x = Mathf.Sin(angle) * xAxis;
    52.        float z = Mathf.Cos(angle) * zAxis;
    53.  
    54.        return new Vector3(x, yAxis, z);
    55.    }
    56. }
    I'm trying to use Unity's new Job system to optimize this, using this ( https://github.com/stella3d/job-sys...ter/Assets/Scripts/AccelerationParallelFor.cs ) as a reference.

    Here is my code

    Code (csharp):
    1.  
    2. public class Simulation : MonoBehaviour
    3. {
    4.    [Header("Sphere Generation Settings")]
    5.    public int count = 100;
    6.    public float radius = 5;
    7.  
    8.    [Header("Sphere Orbitting Settings")]
    9.    public float orbitSpeed = 3f;
    10.    public Vector2 orbitPath;
    11.  
    12.    GameObject[] spheres;
    13.    Vector2[] sphereOrbitPaths;
    14.    Transform[] sphereTransforms;
    15.  
    16.    float[] orbitProgress;
    17.    float[] orbitYAxes;
    18.  
    19.    PositionUpdateJob positionUpdateJob;
    20.    OrbitVelocityJob orbitVelocityJob;
    21.  
    22.    JobHandle positionJobHandle;
    23.    JobHandle orbitJobHandle;
    24.  
    25.    TransformAccessArray transformAccessArray;
    26.    NativeArray<Vector3> orbitalVectors;
    27.    NativeArray<float> orbitalYAxes;
    28.  
    29.    private void Start()
    30.    {
    31.        orbitalVectors = new NativeArray<Vector3>(count, Allocator.Persistent);
    32.  
    33.        spheres = GetComponent<SphereGenerator>().PlaceSpheres(count, radius);
    34.  
    35.        sphereOrbitPaths = new Vector2[count];
    36.        orbitProgress = new float[count];
    37.        sphereTransforms = new Transform[count];
    38.        orbitYAxes = new float[count];
    39.  
    40.        for (int i = 0; i < sphereOrbitPaths.Length; i++)
    41.        {
    42.            sphereOrbitPaths[i] = orbitPath;
    43.            orbitProgress[i] = Random.Range(0f, 1f);
    44.            sphereTransforms[i] = spheres[i].transform;
    45.            orbitYAxes[i] = spheres[i].transform.position.y;
    46.        }
    47.  
    48.        orbitalYAxes = new NativeArray<float>(orbitYAxes, Allocator.Persistent);
    49.  
    50.        transformAccessArray = new TransformAccessArray(sphereTransforms);
    51.    }
    52.  
    53.    private void Update()
    54.    {
    55.        orbitVelocityJob = new OrbitVelocityJob()
    56.        {
    57.            deltaTime = Time.deltaTime,
    58.            orbitSpeed = 0.1f,
    59.            xAxis = orbitPath.x,
    60.            orbitalY = orbitalYAxes,
    61.            zAxis = orbitPath.y,
    62.            orbitProgress = Random.Range(0f, 1f),
    63.            orbitalVector = orbitalVectors
    64.        };
    65.  
    66.        positionUpdateJob = new PositionUpdateJob()
    67.        {
    68.            velocity = orbitalVectors
    69.        };
    70.  
    71.        orbitJobHandle = orbitVelocityJob.Schedule(count, 64);
    72.        positionJobHandle = positionUpdateJob.Schedule(transformAccessArray, orbitJobHandle);
    73.    }
    74.  
    75.    private void LateUpdate()
    76.    {
    77.        positionJobHandle.Complete();
    78.    }
    79.  
    80.    private void OnDestroy()
    81.    {
    82.        transformAccessArray.Dispose();
    83.        orbitalVectors.Dispose();
    84.        orbitalYAxes.Dispose();
    85.    }
    86. }
    87.  
    The jobs:
    Code (csharp):
    1.  
    2. public struct PositionUpdateJob : IJobParallelForTransform
    3. {
    4.    public NativeArray<Vector3> velocity;
    5.  
    6.    public void Execute(int i, TransformAccess transform)
    7.    {
    8.        transform.position = velocity[i];
    9.    }
    10. }
    11.  
    12. public struct OrbitVelocityJob : IJobParallelFor
    13. {
    14.    public float deltaTime;
    15.    public float orbitSpeed;
    16.    public float xAxis;
    17.    public float zAxis;
    18.    public float orbitProgress;
    19.  
    20.    public NativeArray<float> orbitalY;
    21.  
    22.    public NativeArray<Vector3> orbitalVector;
    23.  
    24.    public void Execute(int i)
    25.    {
    26.        var vector = orbitalVector[i];
    27.  
    28.        orbitProgress += deltaTime * orbitSpeed;
    29.        orbitProgress %= 1f;
    30.  
    31.        float angle = Mathf.Deg2Rad * 360f * orbitProgress;
    32.  
    33.        vector.x = Mathf.Sin(angle) * xAxis;
    34.        vector.y = orbitalY[i];
    35.        vector.z = Mathf.Cos(angle) * zAxis;
    36.  
    37.        orbitalVector[i] = vector;
    38.    }
    39. }
    Here are my issues:
    1) The spheres are orbitting faster when using the job system.
    2) No performance improvements in terms of fps, but observed improvements in CPU overhead.

    Is my implementation correct? What am I doing wrong?
     
  2. XRA

    XRA

    Joined:
    Aug 26, 2010
    Posts:
    265
    did you test in a standalone build? I think the Job system, if I understand right, has some safety checking in editor which is great, and when built that stuff is removed. (Please correct me if I'm wrong) I noticed quite a faster speed in standalone with jobs.
     
  3. chuakc92

    chuakc92

    Joined:
    Nov 22, 2016
    Posts:
    20
    Hey, thanks for responding. I increased my objects in the system from 10,000 to 20,000 and it runs about 10-15fps in the editor.

    If I build it, it runs about 29-35 fps without the job system.

    With the job system, it runs 34-40 fps, but the same problem still persists: the spheres are orbitting faster than they should with the job system.
     
    Last edited: Mar 14, 2018
  4. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    you are updating
    orbitProgress
    for each transform in the batch. you should calculate that value only once per frame
     
  5. chuakc92

    chuakc92

    Joined:
    Nov 22, 2016
    Posts:
    20
    Thank you so much! I managed to solve the spheres running faster issue by creating a new NativeArray<float> and initializing it with orbitProgress.

    Code (csharp):
    1.  
    2.     float[] orbitProgress;
    3.     NativeArray<float> orbitProgressNativeArray;
    4.  
    5.     private void Start()
    6.     {
    7.         ....
    8.         orbitProgress = new float[count];
    9.         ....
    10.  
    11.         for (int i = 0; i < sphereOrbitPaths.Length; i++)
    12.         {
    13.             ....
    14.             orbitProgress[i] = Random.Range(0f, 1f);
    15.             ....
    16.         }
    17.         orbitProgressNativeArray = new NativeArray<float>(orbitProgress, Allocator.Persistent);
    18.         ....
    19.     }
    20.  
    Then sending it to orbitVelocityJob, which also has an orbitProgress NativeArray<float>
    Code (csharp):
    1.  
    2.     private void Update()
    3.     {
    4.             orbitVelocityJob = new OrbitVelocityJob()
    5.             {
    6.                 deltaTime = Time.deltaTime,
    7.                 orbitSpeed = 0.1f,
    8.                 xAxis = orbitPath.x,
    9.                 orbitalY = orbitalYAxes,
    10.                 zAxis = orbitPath.y,
    11.                 orbitProgress = orbitProgressNativeArray,
    12.                 orbitalVector = orbitalVectors
    13.             };
    14.             positionUpdateJob = new PositionUpdateJob()
    15.             {
    16.                 velocity = orbitalVectors
    17.             };
    18.             orbitJobHandle = orbitVelocityJob.Schedule(count, 64);
    19.             positionJobHandle = positionUpdateJob.Schedule(transformAccessArray, orbitJobHandle);
    20.     }
    21.  
    There, however, isn't a very significant improvement in framerates. Maybe a 1-2 fps increase.

    Am I doing something wrong?
     
  6. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    Did you profile? Are you sure you are not GPU bound on fps?
     
  7. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,659
    The best thing to do is to look at the CPU Timeline information in the profiler - it will quickly make it clear to you where the time is going.

    Are all your spheres parented to the same root GameObject? IJobParallelForTransform can only parallelise for separate root GameObjects right now.
     
  8. AlkisFortuneFish

    AlkisFortuneFish

    Joined:
    Apr 26, 2013
    Posts:
    973
    Out of curiosity, is this limitation something affecting built-in Unity jobs operating on transforms too? Updating animated transform values, for example?
     
  9. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,659
    I believe so, yep.
     
    AlkisFortuneFish likes this.
  10. AlkisFortuneFish

    AlkisFortuneFish

    Joined:
    Apr 26, 2013
    Posts:
    973
    With that in mind, there really ought to be a way to organise scenes, both in terms of editability and controlling activeInHierarchy that is not identity transform parents.
     
    pvloon likes this.
  11. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,659
    I agree! It's on the list...