profiler
This commit is contained in:
parent
5b2d221d0b
commit
e2fda60745
@ -10,6 +10,10 @@ Execute the contents of the file as if it were REPL input (but allowing multilin
|
||||
|
||||
Execute the expression, but don't print its result.
|
||||
|
||||
## `)profile expr` / `)profile@frequency expr`
|
||||
|
||||
Profile the expression at the given sampling frequency, or 5000 samples/second by default
|
||||
|
||||
## `)t expr` / `)time expr` / `)t:n expr` / `)time:n expr`
|
||||
|
||||
Time the argument expression. `n` specifies the number of times to repeat. Exists to allow not escaping quotes and less overhead for timing very fast & small expressions.
|
||||
|
||||
@ -339,6 +339,22 @@ B gid2str(i32 n) {
|
||||
return r;
|
||||
}
|
||||
|
||||
void* profiler_makeMap() {
|
||||
return m_b2i(64);
|
||||
}
|
||||
i32 profiler_index(void** mapRaw, B comp) {
|
||||
H_b2i* map = *(H_b2i**)mapRaw;
|
||||
i32 r;
|
||||
bool had; u64 p = mk_b2i(&map, comp, &had);
|
||||
if (had) r = map->a[p].val;
|
||||
else r = map->a[p].val = map->pop-1;
|
||||
*(H_b2i**)mapRaw = map;
|
||||
return r;
|
||||
}
|
||||
void profiler_freeMap(void* mapRaw) {
|
||||
free_b2i((H_b2i*)mapRaw);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void fun_gcFn() {
|
||||
|
||||
@ -150,7 +150,7 @@ void fprint(FILE* f, B x) {
|
||||
}
|
||||
else if (isVar(x)) fprintf(f, "(var d=%d i=%d)", (u16)(x.u>>32), (i32)x.u);
|
||||
else if (isExt(x)) fprintf(f, "(extvar d=%d i=%d)", (u16)(x.u>>32), (i32)x.u);
|
||||
else if (x.u==bi_N.u) fprintf(f, "·");
|
||||
else if (x.u==bi_N.u) fprintf(f, "(native ·)");
|
||||
else if (x.u==bi_optOut.u) fprintf(f, "(value optimized out)");
|
||||
else if (x.u==bi_noVar.u) fprintf(f, "(unset variable placeholder)");
|
||||
else if (x.u==bi_okHdr.u) fprintf(f, "(accepted SETH placeholder)");
|
||||
|
||||
@ -36,7 +36,7 @@ __attribute__((optnone))
|
||||
static void* mmap_nvm(u64 sz) {
|
||||
u64 near = (u64)&bqn_exec;
|
||||
u64 MAX_DIST = 1ULL<<30;
|
||||
if (near < MAX_DIST) return mmap(NULL, sz, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_NORESERVE|MAP_PRIVATE|MAP_ANON|MAP_32BIT, -1, 0);
|
||||
if (near < MAX_DIST) return mmap(NULL, sz, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_NORESERVE|MAP_PRIVATE|MAP_ANONYMOUS|MAP_32BIT, -1, 0);
|
||||
u64 ps = getPageSize();
|
||||
|
||||
i32 attempt = 0;
|
||||
@ -50,7 +50,7 @@ static void* mmap_nvm(u64 sz) {
|
||||
#ifdef MAP_FIXED_NOREPLACE
|
||||
noreplace|= MAP_FIXED_NOREPLACE;
|
||||
#endif
|
||||
void* c = mmap((void*)loc, sz, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_NORESERVE|MAP_PRIVATE|MAP_ANON|noreplace, -1, 0);
|
||||
void* c = mmap((void*)loc, sz, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_NORESERVE|MAP_PRIVATE|MAP_ANONYMOUS|noreplace, -1, 0);
|
||||
if (c==NULL) continue;
|
||||
|
||||
i64 dist = (i64)near - (i64)c;
|
||||
@ -241,7 +241,7 @@ u32 perfid = 0;
|
||||
#endif
|
||||
|
||||
static void* nvm_alloc(u64 sz) {
|
||||
// void* r = mmap(NULL, sz, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON|MAP_32BIT, -1, 0);
|
||||
// void* r = mmap(NULL, sz, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_32BIT, -1, 0);
|
||||
// if (r==MAP_FAILED) thrM("JIT: Failed to allocate executable memory");
|
||||
// return r;
|
||||
I8Arr* src = mmX_allocN(fsizeof(I8Arr,a,u8,sz), t_i8arr);
|
||||
|
||||
14
src/load.c
14
src/load.c
@ -116,11 +116,11 @@ Block* load_compObj(B x, B src, B path, Scope* sc) { // consumes x,src
|
||||
}
|
||||
#include "gen/src"
|
||||
#if RT_SRC
|
||||
Block* load_compImport(B bc, B objs, B blocks, B bodies, B inds, B src) { // consumes all
|
||||
return compile(bc, objs, blocks, bodies, inds, bi_N, src, m_str8l("(precompiled)"), NULL);
|
||||
Block* load_compImport(char* name, B bc, B objs, B blocks, B bodies, B inds, B src) { // consumes all
|
||||
return compile(bc, objs, blocks, bodies, inds, bi_N, src, m_str8l(name), NULL);
|
||||
}
|
||||
#else
|
||||
Block* load_compImport(B bc, B objs, B blocks, B bodies) { // consumes all
|
||||
Block* load_compImport(char* name, B bc, B objs, B blocks, B bodies) { // consumes all
|
||||
return compile(bc, objs, blocks, bodies, bi_N, bi_N, bi_N, bi_N, NULL);
|
||||
}
|
||||
#endif
|
||||
@ -350,14 +350,14 @@ void load_init() { // very last init function
|
||||
#ifndef ALL_R0
|
||||
B runtime_0[] = {bi_floor,bi_ceil,bi_stile,bi_lt,bi_gt,bi_ne,bi_ge,bi_rtack,bi_ltack,bi_join,bi_pair,bi_take,bi_drop,bi_select,bi_const,bi_swap,bi_each,bi_fold,bi_atop,bi_over,bi_before,bi_after,bi_cond,bi_repeat};
|
||||
#else
|
||||
Block* runtime0_b = load_compImport(
|
||||
Block* runtime0_b = load_compImport("r0.bqn",
|
||||
#include "gen/runtime0"
|
||||
);
|
||||
B r0r = evalFunBlock(runtime0_b, 0); ptr_dec(runtime0_b);
|
||||
B* runtime_0 = toHArr(r0r)->a;
|
||||
#endif
|
||||
|
||||
Block* runtime_b = load_compImport(
|
||||
Block* runtime_b = load_compImport("r1.bqn",
|
||||
#include "gen/runtime1"
|
||||
);
|
||||
|
||||
@ -458,7 +458,7 @@ void load_init() { // very last init function
|
||||
#else // use compiler
|
||||
B prevAsrt = runtime[n_asrt];
|
||||
runtime[n_asrt] = bi_casrt; // horrible but GC is off so it's fiiiiiine
|
||||
Block* comp_b = load_compImport(
|
||||
Block* comp_b = load_compImport("c.bqn",
|
||||
#include "gen/compiles"
|
||||
);
|
||||
runtime[n_asrt] = prevAsrt;
|
||||
@ -469,7 +469,7 @@ void load_init() { // very last init function
|
||||
|
||||
|
||||
#if FORMATTER
|
||||
Block* fmt_b = load_compImport(
|
||||
Block* fmt_b = load_compImport("f.bqn",
|
||||
#include "gen/formatter"
|
||||
);
|
||||
B fmtM = evalFunBlock(fmt_b, 0); ptr_dec(fmt_b);
|
||||
|
||||
38
src/main.c
38
src/main.c
@ -38,7 +38,22 @@ static bool isCmd(char* s, char** e, const char* cmd) {
|
||||
*e = s;
|
||||
return true;
|
||||
}
|
||||
bool profiler_alloc(void);
|
||||
bool profiler_start(i64 hz);
|
||||
bool profiler_stop(void);
|
||||
void profiler_free(void);
|
||||
void profiler_displayResults(void);
|
||||
|
||||
i64 readInt(char** p) {
|
||||
char* c = *p;
|
||||
i64 am = 0;
|
||||
while (*c>='0' & *c<='9') {
|
||||
am = am*10 + (*c - '0');
|
||||
c++;
|
||||
}
|
||||
*p = c;
|
||||
return am;
|
||||
}
|
||||
void cbqn_runLine0(char* ln, i64 read) {
|
||||
if (ln[0]==10) return;
|
||||
if (ln[read-1]==10) ln[--read] = 0;
|
||||
@ -46,6 +61,7 @@ void cbqn_runLine0(char* ln, i64 read) {
|
||||
B code;
|
||||
int output; // 0-no; 1-formatter; 2-internal
|
||||
i32 time = 0;
|
||||
i64 profile = -1;
|
||||
if (ln[0] == ')') {
|
||||
char* cmdS = ln+1;
|
||||
char* cmdE;
|
||||
@ -60,13 +76,16 @@ void cbqn_runLine0(char* ln, i64 read) {
|
||||
code = fromUTF8l(cmdE);
|
||||
time = -1;
|
||||
output = 0;
|
||||
} else if (isCmd(cmdS, &cmdE, "profile ") || isCmd(cmdS, &cmdE, "profile@")) {
|
||||
char* cpos = cmdE;
|
||||
profile = '@'==*(cpos-1)? readInt(&cpos) : 5000;
|
||||
if (profile==0) { printf("Cannot profile with 0hz sampling frequency\n"); return; }
|
||||
if (profile>999999) { printf("Cannot profile with >999999hz frequency\n"); return; }
|
||||
code = fromUTF8l(cmdE);
|
||||
output = 0;
|
||||
} else if (isCmd(cmdS, &cmdE, "t:") || isCmd(cmdS, &cmdE, "time:")) {
|
||||
char* repE = cmdE;
|
||||
i64 am = 0;
|
||||
while (*repE>='0' & *repE<='9') {
|
||||
am = am*10 + (*repE - '0');
|
||||
repE++;
|
||||
}
|
||||
i64 am = readInt(&repE);
|
||||
if (repE==cmdE) { printf("time command not given repetition count\n"); return; }
|
||||
if (am==0) { printf("repetition count was zero\n"); return; }
|
||||
code = fromUTF8l(repE);
|
||||
@ -187,9 +206,16 @@ void cbqn_runLine0(char* ln, i64 read) {
|
||||
else if (t<1e6) printf("%.4gus\n", t/1e3);
|
||||
else if (t<1e9) printf("%.4gms\n", t/1e6);
|
||||
else printf("%.5gs\n", t/1e9);
|
||||
} else {
|
||||
} else if (profile>0) {
|
||||
if (CATCH) { profiler_stop(); profiler_free(); rethrow(); }
|
||||
if (profiler_alloc() && profiler_start(profile)) {
|
||||
res = execBlockInline(block, gsc);
|
||||
profiler_stop();
|
||||
profiler_displayResults();
|
||||
profiler_free();
|
||||
}
|
||||
popCatch();
|
||||
} else res = execBlockInline(block, gsc);
|
||||
ptr_dec(block);
|
||||
|
||||
if (output) {
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
#define alSize BN(alSize)
|
||||
#ifndef MMAP
|
||||
usz getPageSize();
|
||||
#define MMAP(SZ) mmap(NULL, (SZ)+getPageSize(), PROT_READ|PROT_WRITE, MAP_NORESERVE|MAP_PRIVATE|MAP_ANON, -1, 0)
|
||||
#define MMAP(SZ) mmap(NULL, (SZ)+getPageSize(), PROT_READ|PROT_WRITE, MAP_NORESERVE|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
|
||||
#endif
|
||||
|
||||
typedef struct AllocInfo {
|
||||
|
||||
176
src/vm.c
176
src/vm.c
@ -996,7 +996,7 @@ static void allocStack(void** curr, void** start, void** end, i32 elSize, i32 co
|
||||
#if NO_MMAP
|
||||
void* mem = calloc(sz+ps, 1);
|
||||
#else
|
||||
void* mem = mmap(NULL, sz+ps, PROT_READ|PROT_WRITE, MAP_NORESERVE|MAP_PRIVATE|MAP_ANON, -1, 0);
|
||||
void* mem = mmap(NULL, sz+ps, PROT_READ|PROT_WRITE, MAP_NORESERVE|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
if (*curr == MAP_FAILED) err("Failed to allocate stack");
|
||||
#endif
|
||||
*curr = *start = mem;
|
||||
@ -1231,6 +1231,180 @@ NOINLINE void vm_pstLive() {
|
||||
vm_pst(envStart, envCurr+1);
|
||||
}
|
||||
|
||||
|
||||
#if __has_include(<sys/time.h>) && __has_include(<signal.h>) && !NO_MMAP
|
||||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
#define PROFILE_BUFFER (1ULL<<29)
|
||||
|
||||
typedef struct Profiler_ent {
|
||||
Comp* comp;
|
||||
usz bcPos;
|
||||
} Profiler_ent;
|
||||
Profiler_ent* profiler_buf_s;
|
||||
Profiler_ent* profiler_buf_c;
|
||||
Profiler_ent* profiler_buf_e;
|
||||
bool profile_buf_full;
|
||||
void profiler_sigHandler(int x) {
|
||||
Profiler_ent* bn = profiler_buf_c+1;
|
||||
if (RARE(bn>=profiler_buf_e)) {
|
||||
profile_buf_full = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (envCurr<envStart) return;
|
||||
Env e = *envCurr;
|
||||
Comp* comp = e.sc->body->bl->comp;
|
||||
i32 bcPos = e.pos&1? ((u32)e.pos)>>1 : BCPOS(e.sc->body, (u32*)e.pos);
|
||||
|
||||
*profiler_buf_c = (Profiler_ent){.comp = ptr_inc(comp), .bcPos = bcPos};
|
||||
profiler_buf_c = bn;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool setHandler(bool b) {
|
||||
struct sigaction act = {};
|
||||
act.sa_handler = b? profiler_sigHandler : SIG_DFL;
|
||||
if (sigaction(SIGALRM/*SIGPROF*/, &act, NULL)) {
|
||||
printf("Failed to set profiling signal handler\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static bool setTimer(i64 us) {
|
||||
struct itimerval timer;
|
||||
timer.it_value.tv_sec=0;
|
||||
timer.it_value.tv_usec=us;
|
||||
timer.it_interval.tv_sec=0;
|
||||
timer.it_interval.tv_usec=us;
|
||||
if (setitimer(ITIMER_REAL/*ITIMER_PROF*/, &timer, NULL)) {
|
||||
printf("Failed to start sampling timer\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void* profiler_makeMap();
|
||||
i32 profiler_index(void* mapRaw, B comp);
|
||||
void profiler_freeMap(void* mapRaw);
|
||||
|
||||
bool profiler_alloc() {
|
||||
profiler_buf_s = profiler_buf_c = mmap(NULL, PROFILE_BUFFER, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
if (profiler_buf_s == MAP_FAILED) {
|
||||
fprintf(stderr, "Failed to allocate profiler buffer\n");
|
||||
return false;
|
||||
}
|
||||
profiler_buf_e = profiler_buf_s+PROFILE_BUFFER;
|
||||
profile_buf_full = false;
|
||||
return true;
|
||||
}
|
||||
void profiler_free() {
|
||||
munmap(profiler_buf_s, PROFILE_BUFFER);
|
||||
}
|
||||
|
||||
bool profiler_active;
|
||||
bool profiler_start(i64 hz) {
|
||||
i64 us = 999999/hz;
|
||||
profiler_active = true;
|
||||
return setHandler(true) && setTimer(us);
|
||||
}
|
||||
bool profiler_stop() {
|
||||
if (!profiler_active) return false;
|
||||
profiler_active = false;
|
||||
if (profile_buf_full) fprintf(stderr, "Profiler buffer ran out in the middle of execution. Only timings of the start of profiling will be shown.\n");
|
||||
return setTimer(0) && setHandler(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
usz profiler_getResults(B* compListRes, B* mapListRes, bool keyPath) {
|
||||
Profiler_ent* c = profiler_buf_s;
|
||||
|
||||
B compList = emptyHVec();
|
||||
B mapList = emptyHVec();
|
||||
usz compCount = 0;
|
||||
void* map = profiler_makeMap();
|
||||
|
||||
while (c!=profiler_buf_c) {
|
||||
usz bcPos = c->bcPos;
|
||||
Comp* comp = c->comp;
|
||||
B path = comp->path;
|
||||
if (!q_N(path) && !q_N(comp->src)) {
|
||||
i32 idx = profiler_index(&map, q_N(path)? tag(comp, OBJ_TAG) : path);
|
||||
if (idx == compCount) {
|
||||
compList = vec_addN(compList, tag(comp, OBJ_TAG));
|
||||
i32* rp;
|
||||
usz ia = a(comp->src)->ia;
|
||||
mapList = vec_addN(mapList, m_i32arrv(&rp, ia));
|
||||
for (i32 i = 0; i < ia; i++) rp[i] = 0;
|
||||
compCount++;
|
||||
}
|
||||
B inds = IGetU(comp->indices, 0); usz cs = o2s(IGetU(inds,bcPos));
|
||||
// B inde = IGetU(comp->indices, 1); usz ce = o2s(IGetU(inde,bcPos));
|
||||
i32* cMap = i32arr_ptr(IGetU(mapList, idx));
|
||||
// for (usz i = cs; i <= ce; i++) cMap[i]++;
|
||||
cMap[cs]++;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
profiler_freeMap(map);
|
||||
|
||||
*compListRes = compList;
|
||||
*mapListRes = mapList;
|
||||
return compCount;
|
||||
}
|
||||
|
||||
void profiler_displayResults() {
|
||||
printf("Got "N64u" samples\n", (u64)(profiler_buf_c-profiler_buf_s));
|
||||
|
||||
B compList, mapList;
|
||||
usz compCount = profiler_getResults(&compList, &mapList, true);
|
||||
|
||||
SGetU(compList) SGetU(mapList)
|
||||
for (usz i = 0; i < compCount; i++) {
|
||||
Comp* c = c(Comp, GetU(compList, i));
|
||||
i32* m = i32arr_ptr(GetU(mapList, i));
|
||||
if (!q_N(c->src)) {
|
||||
if (q_N(c->path)) printf("unknown:");
|
||||
else printRaw(c->path);
|
||||
printf(":\n");
|
||||
B src = c->src;
|
||||
SGetU(src)
|
||||
usz sia = a(src)->ia;
|
||||
usz pi = 0;
|
||||
i32 curr = 0;
|
||||
for (usz i = 0; i < sia; i++) {
|
||||
u32 c = o2cu(GetU(src, i));
|
||||
curr+= m[i];
|
||||
if (c=='\n' || i==sia-1) {
|
||||
Arr* sl = TI(src,slice)(inc(src), pi, i-pi); arr_shVec(sl);
|
||||
if (curr==0) printf(" │");
|
||||
else printf("%6d│", curr);
|
||||
printRaw(taga(sl));
|
||||
printf("\n");
|
||||
ptr_dec(sl);
|
||||
curr = 0;
|
||||
pi = i+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dec(compList);
|
||||
dec(mapList);
|
||||
}
|
||||
#else
|
||||
bool profiler_alloc() {
|
||||
printf("Profiler not supported\n");
|
||||
return false;
|
||||
}
|
||||
bool profiler_start(i64 hz) { return false; }
|
||||
bool profiler_stop() { return false; }
|
||||
void profiler_free() { thrM("Profiler not supported"); }
|
||||
usz profiler_getResults(B* compListRes, B* mapListRes, bool keyPath) { thrM("Profiler not supported"); }
|
||||
void profiler_displayResults() { thrM("Profiler not supported"); }
|
||||
#endif
|
||||
|
||||
void unwindEnv(Env* envNew) {
|
||||
assert(envNew<=envCurr);
|
||||
while (envCurr!=envNew) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user