1
0
mirror of https://github.com/golang/go synced 2024-11-21 20:14:52 -07:00

gc: make merely referencing an outer variable in a closure not force heapallocation.

before: runtime_test.BenchmarkCallClosure1       20000000              135 ns/op
after:  runtime_test.BenchmarkCallClosure1      500000000                6 ns/op

R=rsc
CC=golang-dev
https://golang.org/cl/4527091
This commit is contained in:
Luuk van Dijk 2011-06-01 17:02:43 +02:00
parent dcbf59cb4e
commit 2c4edb0eea
5 changed files with 73 additions and 25 deletions

View File

@ -75,7 +75,7 @@ closurebody(NodeList *body)
} }
void void
typecheckclosure(Node *func) typecheckclosure(Node *func, int top)
{ {
Node *oldfn; Node *oldfn;
NodeList *l; NodeList *l;
@ -106,6 +106,10 @@ typecheckclosure(Node *func)
v->op = 0; v->op = 0;
continue; continue;
} }
// For a closure that is called in place, but not
// inside a go statement, avoid moving variables to the heap.
if ((top & (Ecall|Eproc)) == Ecall)
v->heapaddr->etype = 1;
typecheck(&v->heapaddr, Erv); typecheck(&v->heapaddr, Erv);
func->enter = list(func->enter, v->heapaddr); func->enter = list(func->enter, v->heapaddr);
v->heapaddr = N; v->heapaddr = N;

View File

@ -188,6 +188,7 @@ declare(Node *n, int ctxt)
else if(n->op == ONAME) else if(n->op == ONAME)
gen = ++vargen; gen = ++vargen;
pushdcl(s); pushdcl(s);
n->curfn = curfn;
} }
if(ctxt == PAUTO) if(ctxt == PAUTO)
n->xoffset = BADWIDTH; n->xoffset = BADWIDTH;

View File

@ -254,6 +254,7 @@ struct Node
Node* ntype; Node* ntype;
Node* defn; Node* defn;
Node* pack; // real package for import . names Node* pack; // real package for import . names
Node* curfn; // function for local variables
// ONAME func param with PHEAP // ONAME func param with PHEAP
Node* heapaddr; // temp holding heap address of param Node* heapaddr; // temp holding heap address of param
@ -517,15 +518,16 @@ enum
enum enum
{ {
Etop = 1<<1, // evaluated at statement level Etop = 1<<1, // evaluated at statement level
Erv = 1<<2, // evaluated in value context Erv = 1<<2, // evaluated in value context
Etype = 1<<3, Etype = 1<<3,
Ecall = 1<<4, // call-only expressions are ok Ecall = 1<<4, // call-only expressions are ok
Efnstruct = 1<<5, // multivalue function returns are ok Efnstruct = 1<<5, // multivalue function returns are ok
Eiota = 1<<6, // iota is ok Eiota = 1<<6, // iota is ok
Easgn = 1<<7, // assigning to expression Easgn = 1<<7, // assigning to expression
Eindir = 1<<8, // indirecting through expression Eindir = 1<<8, // indirecting through expression
Eaddr = 1<<9, // taking address of expression Eaddr = 1<<9, // taking address of expression
Eproc = 1<<10, // inside a go statement
}; };
#define BITS 5 #define BITS 5
@ -815,7 +817,7 @@ int bset(Bits a, uint n);
*/ */
Node* closurebody(NodeList *body); Node* closurebody(NodeList *body);
void closurehdr(Node *ntype); void closurehdr(Node *ntype);
void typecheckclosure(Node *func); void typecheckclosure(Node *func, int top);
Node* walkclosure(Node *func, NodeList **init); Node* walkclosure(Node *func, NodeList **init);
void walkcallclosure(Node *n, NodeList **init); void walkcallclosure(Node *n, NodeList **init);

View File

@ -29,8 +29,8 @@ static void typecheckfunc(Node*);
static void checklvalue(Node*, char*); static void checklvalue(Node*, char*);
static void checkassign(Node*); static void checkassign(Node*);
static void checkassignlist(NodeList*); static void checkassignlist(NodeList*);
static void stringtoarraylit(Node**); static void stringtoarraylit(Node**);
static Node* resolve(Node*); static Node* resolve(Node*);
static Type* getforwtype(Node*); static Type* getforwtype(Node*);
/* /*
@ -780,7 +780,7 @@ reswitch:
n = r; n = r;
goto reswitch; goto reswitch;
} }
typecheck(&n->left, Erv | Etype | Ecall); typecheck(&n->left, Erv | Etype | Ecall |(top&Eproc));
l = n->left; l = n->left;
if(l->op == ONAME && l->etype != 0) { if(l->op == ONAME && l->etype != 0) {
if(n->isddd && l->etype != OAPPEND) if(n->isddd && l->etype != OAPPEND)
@ -1027,9 +1027,9 @@ reswitch:
// copy([]byte, string) // copy([]byte, string)
if(isslice(n->left->type) && n->right->type->etype == TSTRING) { if(isslice(n->left->type) && n->right->type->etype == TSTRING) {
if (n->left->type->type ==types[TUINT8]) if (n->left->type->type == types[TUINT8])
goto ret; goto ret;
yyerror("arguments to copy have different element types: %lT and string", n->left->type); yyerror("arguments to copy have different element types: %lT and string", n->left->type);
goto error; goto error;
} }
@ -1217,7 +1217,7 @@ reswitch:
case OCLOSURE: case OCLOSURE:
ok |= Erv; ok |= Erv;
typecheckclosure(n); typecheckclosure(n, top);
if(n->type == T) if(n->type == T)
goto error; goto error;
goto ret; goto ret;
@ -1246,11 +1246,15 @@ reswitch:
goto ret; goto ret;
case ODEFER: case ODEFER:
case OPROC:
ok |= Etop; ok |= Etop;
typecheck(&n->left, Etop); typecheck(&n->left, Etop);
goto ret; goto ret;
case OPROC:
ok |= Etop;
typecheck(&n->left, Etop|Eproc);
goto ret;
case OFOR: case OFOR:
ok |= Etop; ok |= Etop;
typechecklist(n->ninit, Etop); typechecklist(n->ninit, Etop);
@ -2165,7 +2169,9 @@ addrescapes(Node *n)
if(n->noescape) if(n->noescape)
break; break;
switch(n->class) { switch(n->class) {
case PAUTO: case PPARAMREF:
addrescapes(n->defn);
break;
case PPARAM: case PPARAM:
case PPARAMOUT: case PPARAMOUT:
// if func param, need separate temporary // if func param, need separate temporary
@ -2173,16 +2179,17 @@ addrescapes(Node *n)
// the function type has already been checked // the function type has already been checked
// (we're in the function body) // (we're in the function body)
// so the param already has a valid xoffset. // so the param already has a valid xoffset.
if(n->class == PPARAM || n->class == PPARAMOUT) {
// expression to refer to stack copy // expression to refer to stack copy
n->stackparam = nod(OPARAM, n, N); n->stackparam = nod(OPARAM, n, N);
n->stackparam->type = n->type; n->stackparam->type = n->type;
n->stackparam->addable = 1; n->stackparam->addable = 1;
if(n->xoffset == BADWIDTH) if(n->xoffset == BADWIDTH)
fatal("addrescapes before param assignment"); fatal("addrescapes before param assignment");
n->stackparam->xoffset = n->xoffset; n->stackparam->xoffset = n->xoffset;
n->xoffset = 0; n->xoffset = 0;
} // fallthrough
case PAUTO:
n->class |= PHEAP; n->class |= PHEAP;
n->addable = 0; n->addable = 0;
@ -2195,7 +2202,9 @@ addrescapes(Node *n)
snprint(buf, sizeof buf, "&%S", n->sym); snprint(buf, sizeof buf, "&%S", n->sym);
n->heapaddr->sym = lookup(buf); n->heapaddr->sym = lookup(buf);
n->heapaddr->class = PHEAP-1; // defer tempname to allocparams n->heapaddr->class = PHEAP-1; // defer tempname to allocparams
curfn->dcl = list(curfn->dcl, n->heapaddr); n->heapaddr->ullman = 1;
n->curfn->dcl = list(n->curfn->dcl, n->heapaddr);
break; break;
} }
break; break;

View File

@ -19,3 +19,35 @@ func BenchmarkCallClosure1(b *testing.B) {
s += func(ii int) int { return 2*ii + j }(i) s += func(ii int) int { return 2*ii + j }(i)
} }
} }
var ss *int
func BenchmarkCallClosure2(b *testing.B) {
for i := 0; i < b.N; i++ {
j := i
s += func() int {
ss = &j
return 2
}()
}
}
func addr1(x int) *int {
return func() *int { return &x }()
}
func BenchmarkCallClosure3(b *testing.B) {
for i := 0; i < b.N; i++ {
ss = addr1(i)
}
}
func addr2() (x int, p *int) {
return 0, func() *int { return &x }()
}
func BenchmarkCallClosure4(b *testing.B) {
for i := 0; i < b.N; i++ {
_, ss = addr2()
}
}