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:
parent
dcbf59cb4e
commit
2c4edb0eea
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user