blob: a63ba04ee121ae4fe80ea69d01740c3f5a0abb89 [file]
// Times what a job costs the frame that runs and joins it, or without threads
// the advances a batch takes: rive_tests "[job-cost]", or run.mjs on the web.
import { Layout, Context } from "rive/host";
import { Job, Jobs } from "rive/jobs";
import { clock } from "rive/os";
const WARMUP: i32 = 20;
const FRAMES: i32 = 300;
const SAMPLES: i32 = 300000;
const STEPPED: i32 = 2000;
@job export function noop(x: i32): i32 {
return x;
}
@job export function samples(count: i32): Float32Array {
let out = new Float32Array(count);
for (let i = 0; i < count; i++) {
out[i] = <f32>i;
}
return out;
}
function ms(began: f64): f64 {
return (clock() - began) * 1000.0;
}
function round(value: f64): string {
return (Math.round(value * 10000.0) / 10000.0).toString();
}
function report(context: Context, name: string, times: Array<f64>): void {
times.sort((a: f64, b: f64): i32 => (a < b ? -1 : a > b ? 1 : 0));
context.log(
"job cost " + name + " median " + round(times[times.length >> 1]) +
" min " + round(times[0]) + " ms",
);
}
export class Main extends Layout {
context: Context? = null;
frame: i32 = 0;
phase: i32 = 0;
missed: i32 = 0;
times: Array<f64> = [];
stepped: Array<Job<i32>> = [];
done: bool = false;
override init(context: Context): bool {
this.context = context;
return true;
}
// Each kind of job has frames to itself, so none pays for what another
// left in the instance it takes.
override advance(seconds: f64): bool {
if (this.done) {
return true;
}
let frame = this.frame++;
if (Jobs.threads() == 0) {
this.advanceStepped(frame);
return true;
}
let phase = this.phase;
let began = clock();
let joined = true;
if (phase == 0) {
joined = Jobs.run(noop, frame).join() != null;
} else if (phase == 1) {
let jobs = new Array<Job<i32>>();
for (let i = 0; i < 6; i++) {
jobs.push(Jobs.run(noop, i));
}
for (let i = 0; i < 6; i++) {
joined = jobs[i].join() != null && joined;
}
} else {
let result = Jobs.run(samples, SAMPLES).join();
joined = result != null && result.value.length == SAMPLES;
}
let took = ms(began);
if (!joined) {
this.missed++;
} else if (frame >= WARMUP) {
this.times.push(took);
}
if (frame < WARMUP + FRAMES - 1) {
return true;
}
let context = this.context!;
report(context, phase == 0 ? "one no-op" : phase == 1 ? "six no-ops" : "1.2 MB result", this.times);
this.times = [];
this.frame = 0;
if (++this.phase == 3) {
context.log("job cost done, " + this.missed.toString() + " frames missed a join");
this.done = true;
}
return true;
}
advanceStepped(frame: i32): void {
let jobs = this.stepped;
if (frame == 0) {
for (let i = 0; i < STEPPED; i++) {
jobs.push(Jobs.run(noop, i));
}
return;
}
if (jobs[jobs.length - 1].running) {
return;
}
this.context!.log(
"job cost stepped " + STEPPED.toString() + " no-ops over " + frame.toString() +
" advances",
);
this.context!.log("job cost done, stepped");
this.done = true;
}
}