This commit is contained in:
dzaima 2022-05-29 22:02:50 +03:00
parent 5b2d221d0b
commit e2fda60745
8 changed files with 241 additions and 21 deletions

View File

@ -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.

View File

@ -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() {

View File

@ -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)");

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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
View File

@ -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) {