Why multiple frames job takes so long at first frame

I write a multiple frames job just like this. When I push space, It takes about 1000+ ms at first frame then takes 10+ ms in the next frames. Is this normal or how can I write a job that cost few time per frame until it completed?

using System;
using System.Collections;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;

public class JobTest : MonoBehaviour
{
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        inputList = new NativeList<int>(1000, Allocator.Persistent);
        for (int i = 0; i < 1000; i++)
        {
            inputList.Add(i);
        }
        outputList = new NativeList<float>(1000, Allocator.Persistent);
    }

    private void OnDestroy()
    {
        inputList.Dispose();
        outputList.Dispose();
    }

    private NativeList<int> inputList;

    private NativeList<float> outputList;

    private bool blocking;

    private JobHandle _handle;
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.Space))
        {
            outputList.Clear();
            outputList.Length = 1000;
            TimeCostJob timeCostJob = new TimeCostJob()
            {
                inputList = inputList,
                outputList = outputList
            };
            _handle = timeCostJob.Schedule(1000, 10, default);
            StartCoroutine(CompleteBlocking());
            Debug.Log($"start at {Time.frameCount}");
        }
    }

    private IEnumerator CompleteBlocking()
    {
        while (!_handle.IsCompleted)
        {
            yield return null;
        }
        _handle.Complete();
        Debug.Log($"complete at {Time.frameCount}");
    }
}

public struct TimeCostJob : IJobParallelFor
{
    [ReadOnly]
    public NativeList<int> inputList;
    [WriteOnly]
    public NativeArray<float> outputList;
    public void Execute(int index)
    {
        for (int i = 0; i < inputList[index]; i++)
        {
            var total = 0f;
            for (int j = 0; j < i; j++)
            {
                var output = math.sin(i) * math.log(i);
                total += output;
            }

            outputList[index] = total;
        }
    }
}

Does not feel right… I’ve done e.g. pixel manipulations with way more data and parallel runs without encountering that.

You do however have extremely uneven jobs unfortunately. Some will have the inner loop 1000 times while another just once. That will probably make the usage of jobs relatively inefficient - try to optimize this.

A few things to experiment with:

  • What safety and debug stuff is enabled in the Jobs menu
  • Does this happen on subsequent calls too (since Unity may compile burst on demand)
  • Does adding “[BurstCompile]” before the IJob struct definition change anything? (that was necessary at some point, I believe and Unity’s own packages do definitely use it)
  • Does this scale with the list size? Like are 300 elements way faster and do 2000 elements take twice as long (or in fact even way longer due to the inner loops)?
  • Does increasing the number of parallel jobs (the second argent of schedule) improve anything

Thanks a lot. It’s something about the param innerLoopBatchCount. When I change it to 100, the job runs very well as less than 10 ms per frame.

Here’s what you should replace this with:

if (_handle.IsCompleted)
    _handle.Complete();

Not just you but any dev ought to get out of the habit of starting coroutines for almost everything when you can do the same with LESS CODE right in Update. :wink:

2 Likes

You are right, it’s a bad habit. Thank you for pointing out this.