You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
323 lines
7.8 KiB
323 lines
7.8 KiB
#include <litc.h>
|
|
|
|
static void gcrun(char * const *cmd, long *_ngcs, long *_xput, double *_gcfrac)
|
|
{
|
|
int p[2];
|
|
if (pipe(p) == -1)
|
|
err(-1, "pipe");
|
|
pid_t c = fork();
|
|
if (c == -1)
|
|
err(-1, "fork");
|
|
if (!c) {
|
|
close(p[0]);
|
|
if (dup2(p[1], 1) == -1)
|
|
err(-1, "dup2");
|
|
close(p[1]);
|
|
|
|
const int cmdsz = 62;
|
|
char *cmds[62+2] = {"time", "-g"};
|
|
int i;
|
|
|
|
for (i = 0; i < cmdsz; i++) {
|
|
cmds[2+i] = cmd[i];
|
|
if (cmd[i] == NULL)
|
|
break;
|
|
}
|
|
execvp(cmds[0], cmds);
|
|
err(-1, "exec");
|
|
}
|
|
close(p[1]);
|
|
|
|
long ngcs = -1;
|
|
long xput = 0;
|
|
double gcfrac = 0;
|
|
|
|
char buf[512];
|
|
ssize_t r;
|
|
int off = 0;
|
|
while ((r = read(p[0], &buf[off], sizeof(buf) - off - 1)) > 0) {
|
|
char *end = &buf[off+r];
|
|
*end = '\0';
|
|
if (strchr(buf, '\n') == NULL) {
|
|
fprintf(stderr, "warning: ignoring long line\n");
|
|
off = 0;
|
|
continue;
|
|
}
|
|
char *nl, *last = buf;
|
|
while ((nl = strchr(last, '\n')) != NULL) {
|
|
*nl = '\0';
|
|
sscanf(last, "GCs: %ld", &ngcs);
|
|
sscanf(last, "GC CPU frac: %lf", &gcfrac);
|
|
sscanf(last, "\tops: %ld", &xput);
|
|
last = nl + 1;
|
|
}
|
|
off = end - last;
|
|
memmove(buf, last, off);
|
|
}
|
|
if (r == -1)
|
|
err(-1, "read");
|
|
|
|
int status;
|
|
if (wait(&status) == -1)
|
|
err(-1, "wait");
|
|
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
|
errx(-1, "child failed");
|
|
close(p[0]);
|
|
|
|
if (_ngcs)
|
|
*_ngcs = ngcs;
|
|
if (_gcfrac)
|
|
*_gcfrac = gcfrac;
|
|
if (_xput)
|
|
*_xput = xput;
|
|
}
|
|
|
|
static void _run(char **cmd)
|
|
{
|
|
pid_t c = fork();
|
|
if (c == -1)
|
|
err(-1, "fork");
|
|
if (!c) {
|
|
int fd = open("/dev/null", O_WRONLY);
|
|
if (fd == -1)
|
|
err(-1, "open");
|
|
if (dup2(fd, 1) == -1)
|
|
err(-1, "dup2");
|
|
close(fd);
|
|
execvp(cmd[0], cmd);
|
|
err(-1, "execvp");
|
|
}
|
|
int status;
|
|
if (wait(&status) == -1)
|
|
err(-1, "wait");
|
|
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
|
errx(-1, "child failed");
|
|
}
|
|
|
|
// format the benchmark command given the parameters. only the most recent
|
|
// command returned is valid.
|
|
static void _mkbmcmd(char **cmd, size_t ncmd, long allocr, long duration)
|
|
{
|
|
if (ncmd < 6)
|
|
errx(-1, "little cmd buf");
|
|
// duration in seconds
|
|
static char dbuf[32];
|
|
snprintf(dbuf, sizeof(dbuf), "-s%ld", duration);
|
|
static char abuf[32];
|
|
snprintf(abuf, sizeof(abuf), "-A%ld", allocr);
|
|
cmd[0] = "sfork";
|
|
cmd[1] = dbuf;
|
|
cmd[2] = "-ba";
|
|
cmd[3] = abuf;
|
|
cmd[4] = "1";
|
|
cmd[5] = NULL;
|
|
}
|
|
|
|
#define GOGC 100
|
|
|
|
// find the xput for a run of benchmark at a particular allocation rate
|
|
__attribute__((unused))
|
|
static long nogcxput(long allocr)
|
|
{
|
|
errx(-1, "do not use; taints GC costs for smaller heaps");
|
|
// set kernel heap size to ~16GB to avoid any gcs
|
|
char *hcmd[] = {"bmgc", "-h", "16000", NULL};
|
|
char *rcmd2[] = {"bmgc", "-g", NULL};
|
|
_run(hcmd);
|
|
_run(rcmd2);
|
|
|
|
const int ncmd = 10;
|
|
char *cmd[ncmd];
|
|
_mkbmcmd(cmd, ncmd, allocr, 10);
|
|
long ngcs, xput;
|
|
int i;
|
|
for (i = 0; i < 10; i++) {
|
|
gcrun(cmd, &ngcs, &xput, NULL);
|
|
if (ngcs == 0)
|
|
break;
|
|
xput = 0;
|
|
}
|
|
if (!xput)
|
|
errx(-1, "failed to get 0 gc xput for allocrate %ld", allocr);
|
|
|
|
// restore old total heap size
|
|
char *rcmd1[] = {"bmgc", "-H", "100", NULL};
|
|
_run(rcmd1);
|
|
_run(rcmd2);
|
|
|
|
return xput;
|
|
}
|
|
|
|
// for a given gc cpu fraction upperbound and allocation rate, find total heap
|
|
// sizing to keep gc cpu time < that gc cput fraction upperbound. returns the
|
|
// total heap size in GOGC terms (all heap sizes are in GOGC terms).
|
|
static int findtotalheapsz(double gcfracub, long allocr, const long targetgcs,
|
|
const long gc0xput)
|
|
{
|
|
// first, get the xput of this allocation rate with 0 gcs. we use that
|
|
// xput to calculate GC CPU time.
|
|
//long gc0xput = nogcxput(allocr);
|
|
printf("0gc xput is %ld\n", gc0xput);
|
|
|
|
// initialize binary search bounds
|
|
#define HIGC 700
|
|
int higc = HIGC;
|
|
int logc = 10;
|
|
// minimum heap size increment
|
|
#define GCINC 10
|
|
const int gcinc = GCINC;
|
|
#define TRIEDSZ ((HIGC/GCINC)+1)
|
|
// byte array tracking whether we tried a certain heap size yet. when
|
|
// we find two adjacents heap sizes that have been tried but had
|
|
// different outcomes (i.e. gc fraction was higher than upper bound for
|
|
// one and lower than upperbound for the other), we have found the
|
|
// target heap size.
|
|
const char _nottried = 0;
|
|
// this slot resulted in gc frac lower than upperbound
|
|
const char _lower = -1;
|
|
// this slot resulted in gc frac higher than upperbound
|
|
const char _higher = 1;
|
|
char tried[TRIEDSZ] = {0};
|
|
|
|
long duration = 10;
|
|
char lastres = _nottried;
|
|
while (1) {
|
|
int curgc = (higc + logc) / 2;
|
|
// round to multiple of incgc
|
|
curgc = (curgc/gcinc)*gcinc;
|
|
int curslot = curgc / gcinc;
|
|
// make sure to round to final slot
|
|
if (tried[curslot] != _nottried) {
|
|
if (lastres == _lower)
|
|
curgc -= gcinc;
|
|
else if (lastres == _higher)
|
|
curgc += gcinc;
|
|
else
|
|
errx(-1, "bad lastres: %d", lastres);
|
|
curslot = curgc/gcinc;
|
|
if (curslot < 0 || curslot >= TRIEDSZ)
|
|
errx(-1, "cannot meet goal gc cpu fraction");
|
|
if (tried[curslot] != _nottried)
|
|
errx(-1, "rounded, but already tried?");
|
|
}
|
|
printf("=== trying GOGC heap size of %d ===\n", curgc);
|
|
char heapszbuf[32];
|
|
snprintf(heapszbuf, sizeof(heapszbuf), "%d", curgc);
|
|
char *resizecmds[] = {"bmgc", "-H", heapszbuf, NULL};
|
|
_run(resizecmds);
|
|
|
|
// run the benchmark for increasing durations of time until we
|
|
// have at least 20 gcs.
|
|
long foundxput;
|
|
while (1) {
|
|
const int ncmds = 32;
|
|
char *cmds[32];
|
|
_mkbmcmd(cmds, ncmds, allocr, duration);
|
|
long xput, ngcs;
|
|
printf("trying allocr %ld with heap %d for %ld "
|
|
"seconds...\n", allocr, curgc, duration);
|
|
gcrun(cmds, &ngcs, &xput, NULL);
|
|
if (ngcs >= targetgcs) {
|
|
printf("good. got %ld gcs\n", ngcs);
|
|
foundxput = xput;
|
|
// prevent duration from growing too large too
|
|
// quickly
|
|
if (ngcs / targetgcs > 1)
|
|
duration /= ngcs / targetgcs;
|
|
break;
|
|
}
|
|
printf("only %ld gcs, trying again...\n", ngcs);
|
|
double gcps = (double)ngcs/duration;
|
|
if (gcps == 0)
|
|
duration = 120;
|
|
else
|
|
duration = targetgcs/gcps + (duration/8);
|
|
|
|
char *prep[] = {"bmgc", "-g", NULL};
|
|
_run(prep);
|
|
}
|
|
|
|
// finally have a run with >20 gcs. calculate gc cpu frac.
|
|
double gcfrac = 1.0 - (double)foundxput/gc0xput;
|
|
if (gcfrac <= gcfracub)
|
|
lastres = _lower;
|
|
else
|
|
lastres = _higher;
|
|
tried[curslot] = lastres;
|
|
|
|
// adjust binary search bounds and duration
|
|
int lastgc = curgc;
|
|
if (lastres == _lower)
|
|
higc = curgc;
|
|
else
|
|
logc = curgc;
|
|
duration *= (double)curgc/lastgc;
|
|
printf(" GC frac: %f\n", gcfrac);
|
|
printf(" adjust heap size %s\n",
|
|
lastres == _lower ? "SMALLER" : "BIGGER");
|
|
|
|
// see if we are done
|
|
int i;
|
|
for (i = 1; i < TRIEDSZ; i++) {
|
|
if (tried[i-1] == _nottried || tried[i] == _nottried)
|
|
continue;
|
|
if (tried[i-1] != tried[i]) {
|
|
printf("FOUND\n");
|
|
int low = (i-1)*gcinc;
|
|
int hi = i*gcinc;
|
|
char st = tried[i-1];
|
|
printf(" heap %d: %s\n", low,
|
|
st == _lower ? "SMALLER" : "BIGGER");
|
|
st = tried[i];
|
|
printf(" heap %d: %s\n", hi,
|
|
st == _lower ? "SMALLER" : "BIGGER");
|
|
return hi;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
__attribute__((unused))
|
|
static void usage()
|
|
{
|
|
fprintf(stderr, "usage: %s [-n target gcs] [-c target gc frac]"
|
|
" -x <0gc xput> <allocr>\n", __progname);
|
|
exit(-1);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
long targetgcs = 20;
|
|
double gctarget = 0.055;
|
|
long gc0x = 0;
|
|
int c;
|
|
while ((c = getopt(argc, argv, "x:n:mc:")) != -1) {
|
|
switch (c) {
|
|
case 'c':
|
|
gctarget = strtod(optarg, NULL);
|
|
if (gctarget < 0 || gctarget > 100.0)
|
|
gctarget = 0.055;
|
|
break;
|
|
case 'n':
|
|
targetgcs = strtol(optarg, NULL, 0);
|
|
if (targetgcs < 0)
|
|
targetgcs = 20;
|
|
break;
|
|
case 'x':
|
|
gc0x = strtol(optarg, NULL, 0);
|
|
break;
|
|
default:
|
|
usage();
|
|
break;
|
|
}
|
|
}
|
|
if (argc - optind != 1 || gc0x <= 0)
|
|
usage();
|
|
long allocr = strtol(argv[optind], NULL, 0);
|
|
if (allocr < 0)
|
|
allocr = 32;
|
|
long idealheap = findtotalheapsz(gctarget, allocr, targetgcs, gc0x);
|
|
printf("ideal heap for allocr %ld: %ld\n", allocr, idealheap);
|
|
return 0;
|
|
}
|