1
0
mirror of https://github.com/golang/go synced 2024-11-21 20:04:44 -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
typecheckclosure(Node *func)
typecheckclosure(Node *func, int top)
{
Node *oldfn;
NodeList *l;
@ -106,6 +106,10 @@ typecheckclosure(Node *func)
v->op = 0;
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);
func->enter = list(func->enter, v->heapaddr);
v->heapaddr = N;

View File

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

View File

@ -254,6 +254,7 @@ struct Node
Node* ntype;
Node* defn;
Node* pack; // real package for import . names
Node* curfn; // function for local variables
// ONAME func param with PHEAP
Node* heapaddr; // temp holding heap address of param
@ -526,6 +527,7 @@ enum
Easgn = 1<<7, // assigning to expression
Eindir = 1<<8, // indirecting through expression
Eaddr = 1<<9, // taking address of expression
Eproc = 1<<10, // inside a go statement
};
#define BITS 5
@ -815,7 +817,7 @@ int bset(Bits a, uint n);
*/
Node* closurebody(NodeList *body);
void closurehdr(Node *ntype);
void typecheckclosure(Node *func);
void typecheckclosure(Node *func, int top);
Node* walkclosure(Node *func, NodeList **init);
void walkcallclosure(Node *n, NodeList **init);

View File

@ -780,7 +780,7 @@ reswitch:
n = r;
goto reswitch;
}
typecheck(&n->left, Erv | Etype | Ecall);
typecheck(&n->left, Erv | Etype | Ecall |(top&Eproc));
l = n->left;
if(l->op == ONAME && l->etype != 0) {
if(n->isddd && l->etype != OAPPEND)
@ -1027,7 +1027,7 @@ reswitch:
// copy([]byte, string)
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;
yyerror("arguments to copy have different element types: %lT and string", n->left->type);
goto error;
@ -1217,7 +1217,7 @@ reswitch:
case OCLOSURE:
ok |= Erv;
typecheckclosure(n);
typecheckclosure(n, top);
if(n->type == T)
goto error;
goto ret;
@ -1246,11 +1246,15 @@ reswitch:
goto ret;
case ODEFER:
case OPROC:
ok |= Etop;
typecheck(&n->left, Etop);
goto ret;
case OPROC:
ok |= Etop;
typecheck(&n->left, Etop|Eproc);
goto ret;
case OFOR:
ok |= Etop;
typechecklist(n->ninit, Etop);
@ -2165,7 +2169,9 @@ addrescapes(Node *n)
if(n->noescape)
break;
switch(n->class) {
case PAUTO:
case PPARAMREF:
addrescapes(n->defn);
break;
case PPARAM:
case PPARAMOUT:
// if func param, need separate temporary
@ -2173,7 +2179,7 @@ addrescapes(Node *n)
// the function type has already been checked
// (we're in the function body)
// so the param already has a valid xoffset.
if(n->class == PPARAM || n->class == PPARAMOUT) {
// expression to refer to stack copy
n->stackparam = nod(OPARAM, n, N);
n->stackparam->type = n->type;
@ -2182,7 +2188,8 @@ addrescapes(Node *n)
fatal("addrescapes before param assignment");
n->stackparam->xoffset = n->xoffset;
n->xoffset = 0;
}
// fallthrough
case PAUTO:
n->class |= PHEAP;
n->addable = 0;
@ -2195,7 +2202,9 @@ addrescapes(Node *n)
snprint(buf, sizeof buf, "&%S", n->sym);
n->heapaddr->sym = lookup(buf);
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;

View File

@ -19,3 +19,35 @@ func BenchmarkCallClosure1(b *testing.B) {
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()
}
}