add quickjs cpu profiling

This commit is contained in:
John Alanbrook 2024-07-24 08:26:29 -05:00
parent ef802bb6f2
commit 90940b42f5
10 changed files with 109 additions and 12 deletions

View file

@ -1509,10 +1509,8 @@ bbox.fromobjs = function(objs)
/* VECTORS */
var Vector = {};
Vector.length = function(v) {
var sum = v.reduce(function(acc, val) { return acc + val**2; }, 0);
return Math.sqrt(sum);
}
Vector.length = function(v) { return Math.hypot(...v); }
Vector.norm = function(v) {
var len = Vector.length(v);
if (!len) return [0,0];

View file

@ -79,6 +79,65 @@ profile.cpu = function(fn, times = 1, q = "unnamed") {
profile.ms = function(t) { return t/1000000; }
profile.secs = function(t) { return t/1000000000; }
var callgraph = {};
var st = profile.now();
function add_callgraph(line, time) {
callgraph[line] ??= 0;
callgraph[line] += time;
}
profile.gather(500, function() {
var time = profile.now()-st;
var err = new Error();
var stack = err.stack.split("\n");
stack = stack.slice(1);
stack = stack.map(x => x.slice(7).split(' '));
var lines = stack.map(x => x[1]).filter(x => x);
lines = lines.map(x => x.slice(1,x.length-1));
lines.forEach(x => add_callgraph(x,time));
st = profile.now();
profile.gather_rate(400+Math.random()*200);
});
var filecache = {};
function get_line(file, line) {
var text = filecache[file];
if (!text) {
var f = io.slurp(file);
if (!f) {
filecache[file] = "undefined";
return filecache[file];
}
filecache[file] = io.slurp(file).split('\n');
text = filecache[file];
}
if (typeof text === 'string') return text;
text = text[Number(line)-1];
if (!text) return "NULL";
return text.trim();
}
prosperon.quit_hook = function()
{
var e = Object.entries(callgraph);
e = e.sort((a,b) => {
if (a[1] > b[1]) return -1;
return 1;
});
e.forEach(x => {
var ffs = x[0].split(':');
var time = profile.best_t(x[1]);
say(x[0] + '::' + time + ':: ' + get_line(ffs[0], ffs[1]));
});
}
/* These controls are available during editing, and during play of debug builds */
debug.inputs = {};
debug.inputs.f1 = function () { debug.draw_phys = !debug.draw_phys; };

View file

@ -651,6 +651,7 @@ prosperon.clipboardpaste = function (str) {};
prosperon.quit = function () {
say(profile.printreport(profcache, "USE REPORT"));
say(profile.printreport(entityreport, "ENTITY REPORT"));
if (prosperon.quit_hook) prosperon.quit_hook();
console.info("QUITTING");
for (var i in debug.log.time)

View file

@ -89,8 +89,7 @@ emitter.step = function(dt)
// update all particles
for (var p of Object.values(this.particles)) {
p.time += dt;
this.step_hook(p);
this.step_hook?.(p);
if (p.time >= p.life) {
this.die_hook(p);

View file

@ -492,7 +492,8 @@ render.init = function() {
}
}
render.sprites = function(gridsize = 1)
render.mixin({
sprites(gridsize = 1)
{
var sps = Object.values(allsprites);
var sprite_buckets = {};
@ -514,7 +515,7 @@ render.sprites = function(gridsize = 1)
render.draw(shape.quad, sprite_ssbo, sparray.length);
}
}
}
}});
render.circle = function(pos, radius, color) {
check_flush();

View file

@ -1194,10 +1194,21 @@ JSC_CCALL(vector_inflate,
arrfree(p);
)
JSC_CCALL(vector_rotate,
HMM_Vec2 vec = js2vec2(argv[0]);
float r = HMM_LenV2(vec);
double angle = js2angle(argv[1]);
angle += atan2(vec.y,vec.x);
vec.x = r*cos(angle);
vec.y = r*sin(angle);
return vec22js(vec);
)
static const JSCFunctionListEntry js_vector_funcs[] = {
MIST_FUNC_DEF(vector,dot,2),
MIST_FUNC_DEF(vector,project,2),
MIST_FUNC_DEF(vector, inflate, 2)
MIST_FUNC_DEF(vector, inflate, 2),
MIST_FUNC_DEF(vector, rotate, 2)
};
JSC_CCALL(game_engine_start, engine_start(argv[0],argv[1], js2number(argv[2]), js2number(argv[3])))
@ -1299,8 +1310,27 @@ static const JSCFunctionListEntry js_audio_funcs[] = {
JSC_CCALL(profile_now, return number2js(stm_now()))
static JSValue instr_v = JS_UNDEFINED;
int iiihandle(JSRuntime *rt, void *data)
{
script_call_sym(instr_v, 0, NULL);
return 0;
}
JSC_CCALL(profile_gather,
int count = js2number(argv[0]);
instr_v = JS_DupValue(js, argv[1]);
JS_SetInterruptHandler(rt, iiihandle, NULL, count);
)
JSC_CCALL(profile_gather_rate,
JS_SetInterruptRate(js2number(argv[0]));
)
static const JSCFunctionListEntry js_profile_funcs[] = {
MIST_FUNC_DEF(profile,now,0),
MIST_FUNC_DEF(profile,gather,2),
MIST_FUNC_DEF(profile,gather_rate,1)
};
JSC_SCALL(io_exists, ret = boolean2js(fexists(str)))

View file

@ -3,6 +3,7 @@
#include "jsffi.h"
#include "stb_ds.h"
#include "resources.h"
#include <sokol/sokol_time.h>
#include <stdarg.h>

View file

@ -9,6 +9,7 @@ extern "C" {
#include <time.h>
extern JSContext *js;
extern JSRuntime *rt;
struct phys_cbs {
JSValue begin;

View file

@ -429,7 +429,7 @@ typedef enum {
/* must be large enough to have a negligible runtime cost and small
enough to call the interrupt callback often. */
#define JS_INTERRUPT_COUNTER_INIT 10000
static int JS_INTERRUPT_COUNTER_INIT = 1000;
struct JSContext {
JSGCObjectHeader header; /* must come first */
@ -1813,10 +1813,16 @@ void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold)
#define free(p) free_is_forbidden(p)
#define realloc(p,s) realloc_is_forbidden(p,s)
void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque)
void JS_SetInterruptRate(int count)
{
JS_INTERRUPT_COUNTER_INIT = count;
}
void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque, int count)
{
rt->interrupt_handler = cb;
rt->interrupt_opaque = opaque;
JS_INTERRUPT_COUNTER_INIT = count;
}
void JS_SetCanBlock(JSRuntime *rt, BOOL can_block)

View file

@ -855,7 +855,8 @@ void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTrac
/* return != 0 if the JS code needs to be interrupted */
typedef int JSInterruptHandler(JSRuntime *rt, void *opaque);
void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque);
void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque, int count);
void JS_SetInterruptRate(int count);
/* if can_block is TRUE, Atomics.wait() can be used */
void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block);
/* set the [IsHTMLDDA] internal slot */