mirror of
https://github.com/golang/go
synced 2024-11-02 09:28:34 +00:00
cmd/gc: allocate select descriptor on stack
benchmark old ns/op new ns/op delta BenchmarkSelectUncontended 220 165 -25.00% BenchmarkSelectContended 209 161 -22.97% BenchmarkSelectProdCons 1042 904 -13.24% But more importantly this change will allow to get rid of free function in runtime. Fixes #6494. LGTM=rsc, khr R=golang-codereviews, rsc, dominik.honnef, khr CC=golang-codereviews, remyoudompheng https://golang.org/cl/107670043
This commit is contained in:
parent
1a116c76ae
commit
40d7d5a656
10 changed files with 98 additions and 39 deletions
|
@ -86,7 +86,7 @@ char *runtimeimport =
|
||||||
"func @\"\".selectnbsend (@\"\".chanType·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (? bool)\n"
|
"func @\"\".selectnbsend (@\"\".chanType·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (? bool)\n"
|
||||||
"func @\"\".selectnbrecv (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".hchan·4 <-chan any) (? bool)\n"
|
"func @\"\".selectnbrecv (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".hchan·4 <-chan any) (? bool)\n"
|
||||||
"func @\"\".selectnbrecv2 (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".received·4 *bool, @\"\".hchan·5 <-chan any) (? bool)\n"
|
"func @\"\".selectnbrecv2 (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".received·4 *bool, @\"\".hchan·5 <-chan any) (? bool)\n"
|
||||||
"func @\"\".newselect (@\"\".size·2 int32) (@\"\".sel·1 *byte)\n"
|
"func @\"\".newselect (@\"\".sel·1 *byte, @\"\".selsize·2 int64, @\"\".size·3 int32)\n"
|
||||||
"func @\"\".selectsend (@\"\".sel·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n"
|
"func @\"\".selectsend (@\"\".sel·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n"
|
||||||
"func @\"\".selectrecv (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n"
|
"func @\"\".selectrecv (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n"
|
||||||
"func @\"\".selectrecv2 (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any, @\"\".received·5 *bool) (@\"\".selected·1 bool)\n"
|
"func @\"\".selectrecv2 (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any, @\"\".received·5 *bool) (@\"\".selected·1 bool)\n"
|
||||||
|
|
|
@ -112,7 +112,7 @@ func selectnbsend(chanType *byte, hchan chan<- any, elem *any) bool
|
||||||
func selectnbrecv(chanType *byte, elem *any, hchan <-chan any) bool
|
func selectnbrecv(chanType *byte, elem *any, hchan <-chan any) bool
|
||||||
func selectnbrecv2(chanType *byte, elem *any, received *bool, hchan <-chan any) bool
|
func selectnbrecv2(chanType *byte, elem *any, received *bool, hchan <-chan any) bool
|
||||||
|
|
||||||
func newselect(size int32) (sel *byte)
|
func newselect(sel *byte, selsize int64, size int32)
|
||||||
func selectsend(sel *byte, hchan chan<- any, elem *any) (selected bool)
|
func selectsend(sel *byte, hchan chan<- any, elem *any) (selected bool)
|
||||||
func selectrecv(sel *byte, hchan <-chan any, elem *any) (selected bool)
|
func selectrecv(sel *byte, hchan <-chan any, elem *any) (selected bool)
|
||||||
func selectrecv2(sel *byte, hchan <-chan any, elem *any, received *bool) (selected bool)
|
func selectrecv2(sel *byte, hchan <-chan any, elem *any, received *bool) (selected bool)
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#include <libc.h>
|
#include <libc.h>
|
||||||
#include "go.h"
|
#include "go.h"
|
||||||
|
|
||||||
|
static Type* selecttype(int32 size);
|
||||||
|
|
||||||
void
|
void
|
||||||
typecheckselect(Node *sel)
|
typecheckselect(Node *sel)
|
||||||
{
|
{
|
||||||
|
@ -95,7 +97,7 @@ void
|
||||||
walkselect(Node *sel)
|
walkselect(Node *sel)
|
||||||
{
|
{
|
||||||
int lno, i;
|
int lno, i;
|
||||||
Node *n, *r, *a, *var, *cas, *dflt, *ch;
|
Node *n, *r, *a, *var, *selv, *cas, *dflt, *ch;
|
||||||
NodeList *l, *init;
|
NodeList *l, *init;
|
||||||
|
|
||||||
if(sel->list == nil && sel->xoffset != 0)
|
if(sel->list == nil && sel->xoffset != 0)
|
||||||
|
@ -257,8 +259,13 @@ walkselect(Node *sel)
|
||||||
|
|
||||||
// generate sel-struct
|
// generate sel-struct
|
||||||
setlineno(sel);
|
setlineno(sel);
|
||||||
var = temp(ptrto(types[TUINT8]));
|
selv = temp(selecttype(sel->xoffset));
|
||||||
r = nod(OAS, var, mkcall("newselect", var->type, nil, nodintconst(sel->xoffset)));
|
selv->esc = EscNone;
|
||||||
|
r = nod(OAS, selv, N);
|
||||||
|
typecheck(&r, Etop);
|
||||||
|
init = list(init, r);
|
||||||
|
var = conv(conv(nod(OADDR, selv, N), types[TUNSAFEPTR]), ptrto(types[TUINT8]));
|
||||||
|
r = mkcall("newselect", T, nil, var, nodintconst(selv->type->width), nodintconst(sel->xoffset));
|
||||||
typecheck(&r, Etop);
|
typecheck(&r, Etop);
|
||||||
init = list(init, r);
|
init = list(init, r);
|
||||||
|
|
||||||
|
@ -301,6 +308,8 @@ walkselect(Node *sel)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// selv is no longer alive after use.
|
||||||
|
r->nbody = list(r->nbody, nod(OVARKILL, selv, N));
|
||||||
r->nbody = concat(r->nbody, cas->nbody);
|
r->nbody = concat(r->nbody, cas->nbody);
|
||||||
r->nbody = list(r->nbody, nod(OBREAK, N, N));
|
r->nbody = list(r->nbody, nod(OBREAK, N, N));
|
||||||
init = list(init, r);
|
init = list(init, r);
|
||||||
|
@ -316,3 +325,50 @@ out:
|
||||||
walkstmtlist(sel->nbody);
|
walkstmtlist(sel->nbody);
|
||||||
lineno = lno;
|
lineno = lno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep in sync with src/pkg/runtime/chan.h.
|
||||||
|
static Type*
|
||||||
|
selecttype(int32 size)
|
||||||
|
{
|
||||||
|
Node *sel, *sudog, *scase, *arr;
|
||||||
|
|
||||||
|
// TODO(dvyukov): it's possible to generate SudoG and Scase only once
|
||||||
|
// and then cache; and also cache Select per size.
|
||||||
|
sudog = nod(OTSTRUCT, N, N);
|
||||||
|
sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("g")), typenod(ptrto(types[TUINT8]))));
|
||||||
|
sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("selectdone")), typenod(ptrto(types[TUINT8]))));
|
||||||
|
sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("link")), typenod(ptrto(types[TUINT8]))));
|
||||||
|
sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8]))));
|
||||||
|
sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64])));
|
||||||
|
typecheck(&sudog, Etype);
|
||||||
|
sudog->type->noalg = 1;
|
||||||
|
sudog->type->local = 1;
|
||||||
|
|
||||||
|
scase = nod(OTSTRUCT, N, N);
|
||||||
|
scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("sg")), sudog));
|
||||||
|
scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("chan")), typenod(ptrto(types[TUINT8]))));
|
||||||
|
scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("pc")), typenod(ptrto(types[TUINT8]))));
|
||||||
|
scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("kind")), typenod(types[TUINT16])));
|
||||||
|
scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("so")), typenod(types[TUINT16])));
|
||||||
|
scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("receivedp")), typenod(ptrto(types[TUINT8]))));
|
||||||
|
typecheck(&scase, Etype);
|
||||||
|
scase->type->noalg = 1;
|
||||||
|
scase->type->local = 1;
|
||||||
|
|
||||||
|
sel = nod(OTSTRUCT, N, N);
|
||||||
|
sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("tcase")), typenod(types[TUINT16])));
|
||||||
|
sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("ncase")), typenod(types[TUINT16])));
|
||||||
|
sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("pollorder")), typenod(ptrto(types[TUINT8]))));
|
||||||
|
sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("lockorder")), typenod(ptrto(types[TUINT8]))));
|
||||||
|
arr = nod(OTARRAY, nodintconst(size), scase);
|
||||||
|
sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("scase")), arr));
|
||||||
|
arr = nod(OTARRAY, nodintconst(size), typenod(ptrto(types[TUINT8])));
|
||||||
|
sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("lockorderarr")), arr));
|
||||||
|
arr = nod(OTARRAY, nodintconst(size), typenod(types[TUINT16]));
|
||||||
|
sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("pollorderarr")), arr));
|
||||||
|
typecheck(&sel, Etype);
|
||||||
|
sel->type->noalg = 1;
|
||||||
|
sel->type->local = 1;
|
||||||
|
|
||||||
|
return sel->type;
|
||||||
|
}
|
||||||
|
|
|
@ -12,5 +12,6 @@ enum {
|
||||||
#else
|
#else
|
||||||
PhysPageSize = 4096,
|
PhysPageSize = 4096,
|
||||||
#endif
|
#endif
|
||||||
PCQuantum = 1
|
PCQuantum = 1,
|
||||||
|
Int64Align = 4
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,5 +20,6 @@ enum {
|
||||||
#endif // Windows
|
#endif // Windows
|
||||||
#endif // Solaris
|
#endif // Solaris
|
||||||
PhysPageSize = 4096,
|
PhysPageSize = 4096,
|
||||||
PCQuantum = 1
|
PCQuantum = 1,
|
||||||
|
Int64Align = 8
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,5 +12,6 @@ enum {
|
||||||
#else
|
#else
|
||||||
PhysPageSize = 4096,
|
PhysPageSize = 4096,
|
||||||
#endif
|
#endif
|
||||||
PCQuantum = 1
|
PCQuantum = 1,
|
||||||
|
Int64Align = 8
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,5 +12,6 @@ enum {
|
||||||
#else
|
#else
|
||||||
PhysPageSize = 4096,
|
PhysPageSize = 4096,
|
||||||
#endif
|
#endif
|
||||||
PCQuantum = 4
|
PCQuantum = 4,
|
||||||
|
Int64Align = 4
|
||||||
};
|
};
|
||||||
|
|
|
@ -434,32 +434,25 @@ func reflect·chanrecv(t *ChanType, c *Hchan, nb bool, elem *byte) (selected boo
|
||||||
selected = chanrecv(t, c, elem, !nb, &received);
|
selected = chanrecv(t, c, elem, !nb, &received);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Select* newselect(int32);
|
static int64
|
||||||
|
selectsize(int32 size)
|
||||||
|
{
|
||||||
|
Select *sel;
|
||||||
|
int64 selsize;
|
||||||
|
|
||||||
#pragma textflag NOSPLIT
|
selsize = sizeof(*sel) +
|
||||||
func newselect(size int32) (sel *byte) {
|
(size-1)*sizeof(sel->scase[0]) +
|
||||||
sel = (byte*)newselect(size);
|
size*sizeof(sel->lockorder[0]) +
|
||||||
|
size*sizeof(sel->pollorder[0]);
|
||||||
|
return ROUND(selsize, Int64Align);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Select*
|
#pragma textflag NOSPLIT
|
||||||
newselect(int32 size)
|
func newselect(sel *Select, selsize int64, size int32) {
|
||||||
{
|
if(selsize != selectsize(size)) {
|
||||||
int32 n;
|
runtime·printf("runtime: bad select size %D, want %D\n", selsize, selectsize(size));
|
||||||
Select *sel;
|
runtime·throw("bad select size");
|
||||||
|
}
|
||||||
n = 0;
|
|
||||||
if(size > 1)
|
|
||||||
n = size-1;
|
|
||||||
|
|
||||||
// allocate all the memory we need in a single allocation
|
|
||||||
// start with Select with size cases
|
|
||||||
// then lockorder with size entries
|
|
||||||
// then pollorder with size entries
|
|
||||||
sel = runtime·mal(sizeof(*sel) +
|
|
||||||
n*sizeof(sel->scase[0]) +
|
|
||||||
size*sizeof(sel->lockorder[0]) +
|
|
||||||
size*sizeof(sel->pollorder[0]));
|
|
||||||
|
|
||||||
sel->tcase = size;
|
sel->tcase = size;
|
||||||
sel->ncase = 0;
|
sel->ncase = 0;
|
||||||
sel->lockorder = (void*)(sel->scase + size);
|
sel->lockorder = (void*)(sel->scase + size);
|
||||||
|
@ -467,7 +460,6 @@ newselect(int32 size)
|
||||||
|
|
||||||
if(debug)
|
if(debug)
|
||||||
runtime·printf("newselect s=%p size=%d\n", sel, size);
|
runtime·printf("newselect s=%p size=%d\n", sel, size);
|
||||||
return sel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// cut in half to give stack a chance to split
|
// cut in half to give stack a chance to split
|
||||||
|
@ -960,7 +952,6 @@ retc:
|
||||||
}
|
}
|
||||||
if(cas->sg.releasetime > 0)
|
if(cas->sg.releasetime > 0)
|
||||||
runtime·blockevent(cas->sg.releasetime - t0, 2);
|
runtime·blockevent(cas->sg.releasetime - t0, 2);
|
||||||
runtime·free(sel);
|
|
||||||
return pc;
|
return pc;
|
||||||
|
|
||||||
sclose:
|
sclose:
|
||||||
|
@ -997,7 +988,9 @@ func reflect·rselect(cases Slice) (chosen int, recvOK bool) {
|
||||||
|
|
||||||
rcase = (runtimeSelect*)cases.array;
|
rcase = (runtimeSelect*)cases.array;
|
||||||
|
|
||||||
sel = newselect(cases.len);
|
// FlagNoScan is safe here, because all objects are also referenced from cases.
|
||||||
|
sel = runtime·mallocgc(selectsize(cases.len), 0, FlagNoScan);
|
||||||
|
runtime·newselect(sel, selectsize(cases.len), cases.len);
|
||||||
for(i=0; i<cases.len; i++) {
|
for(i=0; i<cases.len; i++) {
|
||||||
rc = &rcase[i];
|
rc = &rcase[i];
|
||||||
switch(rc->dir) {
|
switch(rc->dir) {
|
||||||
|
|
|
@ -9,13 +9,15 @@ typedef struct SudoG SudoG;
|
||||||
typedef struct Select Select;
|
typedef struct Select Select;
|
||||||
typedef struct Scase Scase;
|
typedef struct Scase Scase;
|
||||||
|
|
||||||
|
// Known to compiler.
|
||||||
|
// Changes here must also be made in src/cmd/gc/select.c's selecttype.
|
||||||
struct SudoG
|
struct SudoG
|
||||||
{
|
{
|
||||||
G* g;
|
G* g;
|
||||||
uint32* selectdone;
|
uint32* selectdone;
|
||||||
SudoG* link;
|
SudoG* link;
|
||||||
int64 releasetime;
|
|
||||||
byte* elem; // data element
|
byte* elem; // data element
|
||||||
|
int64 releasetime;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WaitQ
|
struct WaitQ
|
||||||
|
@ -55,6 +57,8 @@ enum
|
||||||
CaseDefault,
|
CaseDefault,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Known to compiler.
|
||||||
|
// Changes here must also be made in src/cmd/gc/select.c's selecttype.
|
||||||
struct Scase
|
struct Scase
|
||||||
{
|
{
|
||||||
SudoG sg; // must be first member (cast to Scase)
|
SudoG sg; // must be first member (cast to Scase)
|
||||||
|
@ -65,6 +69,8 @@ struct Scase
|
||||||
bool* receivedp; // pointer to received bool (recv2)
|
bool* receivedp; // pointer to received bool (recv2)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Known to compiler.
|
||||||
|
// Changes here must also be made in src/cmd/gc/select.c's selecttype.
|
||||||
struct Select
|
struct Select
|
||||||
{
|
{
|
||||||
uint16 tcase; // total count of scase[]
|
uint16 tcase; // total count of scase[]
|
||||||
|
|
|
@ -138,7 +138,7 @@ var b bool
|
||||||
|
|
||||||
// this used to have a spurious "live at entry to f11a: ~r0"
|
// this used to have a spurious "live at entry to f11a: ~r0"
|
||||||
func f11a() *int {
|
func f11a() *int {
|
||||||
select { // ERROR "live at call to selectgo: autotmp"
|
select { // ERROR "live at call to newselect: autotmp" "live at call to selectgo: autotmp"
|
||||||
case <-c: // ERROR "live at call to selectrecv: autotmp"
|
case <-c: // ERROR "live at call to selectrecv: autotmp"
|
||||||
return nil
|
return nil
|
||||||
case <-c: // ERROR "live at call to selectrecv: autotmp"
|
case <-c: // ERROR "live at call to selectrecv: autotmp"
|
||||||
|
@ -153,7 +153,7 @@ func f11b() *int {
|
||||||
// get to the bottom of the function.
|
// get to the bottom of the function.
|
||||||
// This used to have a spurious "live at call to printint: p".
|
// This used to have a spurious "live at call to printint: p".
|
||||||
print(1) // nothing live here!
|
print(1) // nothing live here!
|
||||||
select { // ERROR "live at call to selectgo: autotmp"
|
select { // ERROR "live at call to newselect: autotmp" "live at call to selectgo: autotmp"
|
||||||
case <-c: // ERROR "live at call to selectrecv: autotmp"
|
case <-c: // ERROR "live at call to selectrecv: autotmp"
|
||||||
return nil
|
return nil
|
||||||
case <-c: // ERROR "live at call to selectrecv: autotmp"
|
case <-c: // ERROR "live at call to selectrecv: autotmp"
|
||||||
|
@ -170,7 +170,7 @@ func f11c() *int {
|
||||||
// Unlike previous, the cases in this select fall through,
|
// Unlike previous, the cases in this select fall through,
|
||||||
// so we can get to the println, so p is not dead.
|
// so we can get to the println, so p is not dead.
|
||||||
print(1) // ERROR "live at call to printint: p"
|
print(1) // ERROR "live at call to printint: p"
|
||||||
select { // ERROR "live at call to newselect: p" "live at call to selectgo: autotmp.* p"
|
select { // ERROR "live at call to newselect: autotmp.* p" "live at call to selectgo: autotmp.* p"
|
||||||
case <-c: // ERROR "live at call to selectrecv: autotmp.* p"
|
case <-c: // ERROR "live at call to selectrecv: autotmp.* p"
|
||||||
case <-c: // ERROR "live at call to selectrecv: autotmp.* p"
|
case <-c: // ERROR "live at call to selectrecv: autotmp.* p"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue