Got a multiple-frame test job working using what I could find on forum posts of the subject, but what I can’t figure out is how to get this example working inside Systembase (as JobComponentSystem is being phased out)
- my problem being that with systembase it requires variables to be declared on every OnUpdate, so it destorys my result from the job. Am I missing something?
public class TestAsync2System : JobComponentSystem
{
private enum SystemStatus
{
Idle,
Busy,
}
private SystemStatus systemStatus = SystemStatus.Idle;
private JobHandle jobHandle;
NativeArray<float> result;
protected override JobHandle OnUpdate(JobHandle inputDependencies)
{
if (systemStatus == SystemStatus.Busy && jobHandle.IsCompleted)
{
jobHandle.Complete();
systemStatus = SystemStatus.Idle;
Debug.Log("Completed the job, result="+result[0]);
}
if (systemStatus == SystemStatus.Idle)
{
Debug.Log("Schedules the job");
if (result.IsCreated) { result.Dispose(); }
result = new NativeArray<float>(1, Allocator.Persistent);
var AJob = new AJob { result = result };
jobHandle = AJob.Schedule(inputDependencies);
systemStatus = SystemStatus.Busy;
}
else
{
Debug.Log("Still working on job");
}
return inputDependencies;
}
struct AJob : IJob
{
public NativeArray<float> result;
public void Execute()
{
for (long i = 0; i < 100000000; i++) {result[0] = math.pow(123.23f, 2.3f) * math.sqrt(1235.12f); }
}
}
protected override void OnDestroy()
{
base.OnDestroy();
jobHandle.Complete();
if (result.IsCreated) { result.Dispose(); }
}
}
Nope, never mind, this works… the problem with return values was before I changed from .WithCode to calling an Ijob.
Working code for long running job inside Systembase, if anyone needs:
public class TestAsync2System : SystemBase
{
private enum SystemStatus
{
Idle,
Busy,
}
private SystemStatus systemStatus = SystemStatus.Idle;
private JobHandle jobHandle;
NativeArray<float> result;
protected override void OnUpdate()
{
if (systemStatus == SystemStatus.Busy && jobHandle.IsCompleted)
{
jobHandle.Complete();
systemStatus = SystemStatus.Idle;
Debug.Log("Completed the job, result="+result[0]);
}
if (systemStatus == SystemStatus.Idle)
{
Debug.Log("Schedules the job");
if (result.IsCreated) { result.Dispose(); }
result = new NativeArray<float>(1, Allocator.Persistent);
var AJob = new AJob { result = result };
jobHandle = AJob.Schedule();
systemStatus = SystemStatus.Busy;
}
else
{
Debug.Log("Still working on job");
}
return;
}
struct AJob : IJob
{
public NativeArray<float> result;
public void Execute()
{
for (long i = 0; i < 100000000; i++) {result[0] = math.pow(123.23f, 2.3f) * math.sqrt(1235.12f); }
}
}
protected override void OnDestroy()
{
base.OnDestroy();
jobHandle.Complete();
if (result.IsCreated) { result.Dispose(); }
}
}
2 Likes
and here the async job is working as a Job.WithCode as well. My original assumption that the result from the job is destroyed because a new variable is declared every update was wrong. Apparently that assignment is a pointer, so whenever the update-variable is updated, the “original” is updated as well - here as _result VS result. Yeah, I’m still a c# noob.
public class TestAsync2System : SystemBase
{
private enum SystemStatus
{
Idle,
Busy,
}
private SystemStatus systemStatus = SystemStatus.Idle;
private JobHandle jobHandle;
private NativeArray<float> _result;
private float timer;
private NativeArray<float> _number;
protected override void OnCreate()
{
_number = new NativeArray<float>(1, Allocator.Persistent);
}
protected override void OnUpdate()
{
var result = _result;
if (systemStatus == SystemStatus.Busy && jobHandle.IsCompleted)
{
jobHandle.Complete();
systemStatus = SystemStatus.Idle;
Debug.Log("Completed the job in time, " + round(timer * 10 ) / 10 + ". Result = " + result[0]);
}
if (systemStatus == SystemStatus.Idle)
{
Debug.Log("Schedules the job");
timer = 0;
if (result.IsCreated) { result.Dispose(); }
_result = new NativeArray<float>(1, Allocator.Persistent);
result = _result;
var number = _number;
number[0] += 0.1f;
jobHandle = Job.WithCode(()=>
{
for (long i = 0; i < 100000000; i++)
{
result[0] = math.pow(number[0], number[0]+0.2f) * math.sqrt(number[0]);
}
}).Schedule(new JobHandle());
systemStatus = SystemStatus.Busy;
}
else
{
Debug.Log("Still working on job");
timer += Time.DeltaTime;
}
}
protected override void OnDestroy()
{
base.OnDestroy();
jobHandle.Complete();
if (_result.IsCreated) { _result.Dispose(); }
if (_number.IsCreated) { _number.Dispose(); }
}
}
1 Like
Wait, there’s something fishy here. With my understanding the following code with IJob should be the same as above using Job.WithCode, but in these examples, when I don’t update the number variable between loops, the IJob runs the 100 mill loop in less than 0.1 seconds, while the Job.WithCode runs at 6.7 seconds - which is the same as the IJob if I do update the number variable between loops or even inside the loop - which makes me guess it has to do with some cache that in this example works well in the IJob, but doesn’t kick in for the Job.WithCode?
@Joachim_Ante_1 and @DreamingImLatios , is that correct, or can you point out a fault in my Job.WithCode maybe? I’d appreciate it.
public class TestAsync2System : SystemBase
{
private enum SystemStatus
{
Idle,
Busy,
}
private SystemStatus systemStatus = SystemStatus.Idle;
private JobHandle jobHandle;
NativeArray<float> _result;
private float timer;
protected override void OnUpdate()
{
var result = _result;
if (systemStatus == SystemStatus.Busy && jobHandle.IsCompleted)
{
jobHandle.Complete();
systemStatus = SystemStatus.Idle;
Debug.Log("Completed the job in time, " + round(timer * 10 ) / 10 + ". Result = " + result[0]);
}
if (systemStatus == SystemStatus.Idle)
{
Debug.Log("Schedules the job");
timer = 0;
if (result.IsCreated) { result.Dispose(); }
_result = new NativeArray<float>(1, Allocator.Persistent);
result = _result;
var AJob = new AJob { result = result };
jobHandle = AJob.Schedule();
systemStatus = SystemStatus.Busy;
}
else
{
Debug.Log("Still working on job");
timer += Time.DeltaTime;
}
//result.Dispose();
}
[BurstCompile]
struct AJob : IJob
{
public NativeArray<float> result;
private float number;
public void Execute()
{
number = 0.1f;
for (long i = 0; i < 100000000; i++)
{
result[0] = math.pow(number, number+0.2f) * math.sqrt(number);
}
}
}
protected override void OnDestroy()
{
base.OnDestroy();
jobHandle.Complete();
if (_result.IsCreated) { _result.Dispose(); }
}
}
2 Likes
If this really is the code you are profiling, it looks like in the Job.WithCode based on what you said, there may be aliasing between number[0] and result[0]. This is either a bug in codegen not providing proper aliasing info, or Burst not detecting the loop can be optimized out.
1 Like
Yeah, I don’t know why it does so, but I found that difference. I tried to define the number as a float inside the job now, and then it was back to less than 0.1s. So it’s not the Job.WithCode itself, rather using a collection in the forumla rather than a float. Thanks for your quick input, Latios.
public class TestAsync2SystemWithCode : SystemBase
{
private enum SystemStatus
{
Idle,
Busy,
}
private SystemStatus systemStatus = SystemStatus.Idle;
private JobHandle jobHandle;
private NativeArray<float> _result;
private float timer;
protected override void OnUpdate()
{
var result = _result;
if (systemStatus == SystemStatus.Busy && jobHandle.IsCompleted)
{
jobHandle.Complete();
systemStatus = SystemStatus.Idle;
Debug.Log("Completed the job in time, " + round(timer * 10 ) / 10 + ". Result = " + result[0]);
}
if (systemStatus == SystemStatus.Idle)
{
Debug.Log("Schedules the job");
timer = 0;
if (result.IsCreated) { result.Dispose(); }
_result = new NativeArray<float>(1, Allocator.Persistent);
result = _result;
jobHandle = Job.WithCode(()=>
{
float num = 0.1f;
for (long i = 0; i < 100000000; i++)
{
result[0] = math.pow(num, num+0.2f) * math.sqrt(num);
}
}).WithBurst().Schedule(new JobHandle());
systemStatus = SystemStatus.Busy;
}
else
{
Debug.Log("Still working on job");
timer += Time.DeltaTime;
}
}
protected override void OnDestroy()
{
base.OnDestroy();
jobHandle.Complete();
if (_result.IsCreated) { _result.Dispose(); }
}
}
1 Like
You have to dispose NativeArrays in SystemBase? I thought one of the benefits of using this over Monobehaviour was to not having to do that explicitly?
Depends on your allocation. For async job you’d want it to live some frames, and thus need to allocate it with persistent.
_result = new NativeArray(1, Allocator.Persistent);
1 Like