Skip to content

Commit

Permalink
cmd/gc: make liveness ~10x faster
Browse files Browse the repository at this point in the history
1) The arrayindexof lookup function is O(n). Replace with O(1) lookups.

2) The checkptxt function is O(n²) and is purely for debugging.
Only run when the debugging flags are turned on.

3) Iterating over sparse bitmaps can be done faster word by word.
Introduce and use bvnext for that.

Run times before and after, on my 2.5 GHz Core i5 MacBook Pro.

x.go       9.48  0.84  issue 8259

x100.go    0.01  0.01  issue 8354
x1000.go   0.10  0.10
x2000.go   0.62  0.19
x3000.go   1.33  0.34
x4000.go   2.29  0.49
x5000.go   3.89  0.67
x6000.go   5.00  0.90
x7000.go   6.70  1.13
x8000.go   9.44  1.38
x9000.go  11.23  1.87
x10000.go 13.78  2.09

Fixes #8259.
Fixes #8354.

LGTM=iant, r
R=golang-codereviews, iant, r
CC=golang-codereviews
https://golang.org/cl/125720043
  • Loading branch information
rsc committed Aug 6, 2014
1 parent c1fcdb0 commit 7aa3031
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 35 deletions.
14 changes: 0 additions & 14 deletions src/cmd/gc/array.c
Expand Up @@ -108,20 +108,6 @@ arrayadd(Array *array, void *element)
arrayset(array, array->length - 1, element);
}

int32
arrayindexof(Array *array, void *element)
{
void *p;
int32 i;

for(i = 0; i < array->length; i++) {
p = arrayget(array, i);
if(memcmp(p, &element, array->size) == 0)
return i;
}
return -1;
}

void
arraysort(Array *array, int (*cmp)(const void*, const void*))
{
Expand Down
36 changes: 30 additions & 6 deletions src/cmd/gc/bv.c
Expand Up @@ -9,6 +9,8 @@
enum {
WORDSIZE = sizeof(uint32),
WORDBITS = 32,
WORDMASK = WORDBITS - 1,
WORDSHIFT = 5,
};

static uintptr
Expand Down Expand Up @@ -94,13 +96,35 @@ bvconcat(Bvec *src1, Bvec *src2)
int
bvget(Bvec *bv, int32 i)
{
uint32 mask, word;

if(i < 0 || i >= bv->n)
fatal("bvget: index %d is out of bounds with length %d\n", i, bv->n);
mask = 1U << (i % WORDBITS);
word = bv->b[i / WORDBITS] & mask;
return word ? 1 : 0;
return (bv->b[i>>WORDSHIFT] >> (i&WORDMASK)) & 1;
}

// bvnext returns the smallest index >= i for which bvget(bv, i) == 1.
// If there is no such index, bvnext returns -1.
int
bvnext(Bvec *bv, int32 i)
{
uint32 w;

// Jump i ahead to next word with bits.
if((bv->b[i>>WORDSHIFT]>>(i&WORDMASK)) == 0) {
i &= ~WORDMASK;
i += WORDBITS;
while(i < bv->n && bv->b[i>>WORDSHIFT] == 0)
i += WORDBITS;
}
if(i >= bv->n)
return -1;

// Find 1 bit.
w = bv->b[i>>WORDSHIFT]>>(i&WORDMASK);
while((w&1) == 0) {
w>>=1;
i++;
}
return i;
}

int
Expand All @@ -109,7 +133,7 @@ bvisempty(Bvec *bv)
int32 i;

for(i = 0; i < bv->n; i += WORDBITS)
if(bv->b[i / WORDBITS] != 0)
if(bv->b[i>>WORDSHIFT] != 0)
return 0;
return 1;
}
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/gc/go.h
Expand Up @@ -1017,7 +1017,6 @@ int32 arraylength(Array *array);
void* arrayget(Array *array, int32 index);
void arrayset(Array *array, int32 index, void *element);
void arrayadd(Array *array, void *element);
int32 arrayindexof(Array* array, void *element);
void arraysort(Array* array, int (*cmp)(const void*, const void*));

/*
Expand All @@ -1043,6 +1042,7 @@ int bvcmp(Bvec *bv1, Bvec *bv2);
void bvcopy(Bvec *dst, Bvec *src);
Bvec* bvconcat(Bvec *src1, Bvec *src2);
int bvget(Bvec *bv, int32 i);
int32 bvnext(Bvec *bv, int32 i);
int bvisempty(Bvec *bv);
void bvnot(Bvec *bv);
void bvor(Bvec *dst, Bvec *src1, Bvec *src2);
Expand Down
52 changes: 38 additions & 14 deletions src/cmd/gc/plive.c
Expand Up @@ -283,13 +283,30 @@ getvariables(Node *fn)
// For arguments and results, the bitmap covers all variables,
// so we must include all the variables, even the ones without
// pointers.
//
// The Node.opt field is available for use by optimization passes.
// We use it to hold the index of the node in the variables array, plus 1
// (so that 0 means the Node is not in the variables array).
// Each pass should clear opt when done, but you never know,
// so clear them all ourselves too.
// The Node.curfn field is supposed to be set to the current function
// already, but for some compiler-introduced names it seems not to be,
// so fix that here.
// Later, when we want to find the index of a node in the variables list,
// we will check that n->curfn == curfn and n->opt > 0. Then n->opt - 1
// is the index in the variables list.
ll->n->opt = nil;
ll->n->curfn = curfn;
switch(ll->n->class) {
case PAUTO:
if(haspointers(ll->n->type))
if(haspointers(ll->n->type)) {
ll->n->opt = (void*)(uintptr)(arraylength(result)+1);
arrayadd(result, &ll->n);
}
break;
case PPARAM:
case PPARAMOUT:
ll->n->opt = (void*)(uintptr)(arraylength(result)+1);
arrayadd(result, &ll->n);
break;
}
Expand Down Expand Up @@ -718,14 +735,16 @@ progeffects(Prog *prog, Array *vars, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
}
if(info.flags & (LeftRead | LeftWrite | LeftAddr)) {
from = &prog->from;
if (from->node != nil && from->sym != nil) {
if (from->node != nil && from->sym != nil && from->node->curfn == curfn) {
switch(from->node->class & ~PHEAP) {
case PAUTO:
case PPARAM:
case PPARAMOUT:
pos = arrayindexof(vars, from->node);
pos = (int)(uintptr)from->node->opt - 1; // index in vars
if(pos == -1)
goto Next;
if(pos >= arraylength(vars) || *(Node**)arrayget(vars, pos) != from->node)
fatal("bad bookkeeping in liveness %N %d", from->node, pos);
if(from->node->addrtaken) {
bvset(avarinit, pos);
} else {
Expand All @@ -741,14 +760,16 @@ progeffects(Prog *prog, Array *vars, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
Next:
if(info.flags & (RightRead | RightWrite | RightAddr)) {
to = &prog->to;
if (to->node != nil && to->sym != nil) {
if (to->node != nil && to->sym != nil && to->node->curfn == curfn) {
switch(to->node->class & ~PHEAP) {
case PAUTO:
case PPARAM:
case PPARAMOUT:
pos = arrayindexof(vars, to->node);
pos = (int)(uintptr)to->node->opt - 1; // index in vars
if(pos == -1)
goto Next1;
if(pos >= arraylength(vars) || *(Node**)arrayget(vars, pos) != to->node)
fatal("bad bookkeeping in liveness %N %d", to->node, pos);
if(to->node->addrtaken) {
if(prog->as != AVARKILL)
bvset(avarinit, pos);
Expand Down Expand Up @@ -1020,6 +1041,9 @@ checkptxt(Node *fn, Prog *firstp)
{
Prog *p;

if(debuglive == 0)
return;

for(p = firstp; p != P; p = p->link) {
if(0)
print("analyzing '%P'\n", p);
Expand Down Expand Up @@ -1172,21 +1196,17 @@ twobitlivepointermap(Liveness *lv, Bvec *liveout, Array *vars, Bvec *args, Bvec
vlong xoffset;
int32 i;

for(i = 0; i < arraylength(vars); i++) {
for(i = 0; (i = bvnext(liveout, i)) >= 0; i++) {
node = *(Node**)arrayget(vars, i);
switch(node->class) {
case PAUTO:
if(bvget(liveout, i)) {
xoffset = node->xoffset + stkptrsize;
twobitwalktype1(node->type, &xoffset, locals);
}
xoffset = node->xoffset + stkptrsize;
twobitwalktype1(node->type, &xoffset, locals);
break;
case PPARAM:
case PPARAMOUT:
if(bvget(liveout, i)) {
xoffset = node->xoffset;
twobitwalktype1(node->type, &xoffset, args);
}
xoffset = node->xoffset;
twobitwalktype1(node->type, &xoffset, args);
break;
}
}
Expand Down Expand Up @@ -1944,6 +1964,7 @@ liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
Array *cfg, *vars;
Liveness *lv;
int debugdelta;
NodeList *l;

// Change name to dump debugging information only for a specific function.
debugdelta = 0;
Expand Down Expand Up @@ -1984,6 +2005,9 @@ liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
twobitwritesymbol(lv->argslivepointers, argssym);

// Free everything.
for(l=fn->dcl; l != nil; l = l->next)
if(l->n != N)
l->n->opt = nil;
freeliveness(lv);
arrayfree(vars);
freecfg(cfg);
Expand Down

0 comments on commit 7aa3031

Please sign in to comment.