aboutsummaryrefslogtreecommitdiff
path: root/src/pkg/runtime/chan.c
diff options
context:
space:
mode:
authorDmitriy Vyukov <dvyukov@google.com>2011-07-20 11:51:25 -0400
committerRuss Cox <rsc@golang.org>2011-07-20 11:51:25 -0400
commit90f3cb13fb0f37073e02ecd79771deb938ea7f5e (patch)
tree48244a1dc614b2e8e70b0b237b616ce6eea68945 /src/pkg/runtime/chan.c
parentba2e3af1778da52340a9f3f7dd7262e5ebf64055 (diff)
downloadgo-90f3cb13fb0f37073e02ecd79771deb938ea7f5e.tar.xz
runtime: improve performance of sync channels
1. SudoG always contains a pointer to the element (thus no variable size, and less copying). 2. chansend/chanrecv allocate SudoG on the stack. 3. Copying of elements and gorotuine notifications are moved out of critical sections. benchmark old ns/op new ns/op delta BenchmarkSelectUncontended 515.00 514.00 -0.19% BenchmarkSelectUncontended-2 291.00 281.00 -3.44% BenchmarkSelectUncontended-4 213.00 189.00 -11.27% BenchmarkSelectUncontended-8 78.30 79.00 +0.89% BenchmarkSelectContended 518.00 514.00 -0.77% BenchmarkSelectContended-2 655.00 631.00 -3.66% BenchmarkSelectContended-4 1026.00 1051.00 +2.44% BenchmarkSelectContended-8 2026.00 2128.00 +5.03% BenchmarkSelectNonblock 175.00 173.00 -1.14% BenchmarkSelectNonblock-2 85.10 87.70 +3.06% BenchmarkSelectNonblock-4 60.10 43.30 -27.95% BenchmarkSelectNonblock-8 37.60 25.50 -32.18% BenchmarkChanUncontended 109.00 114.00 +4.59% BenchmarkChanUncontended-2 54.60 57.20 +4.76% BenchmarkChanUncontended-4 27.40 28.70 +4.74% BenchmarkChanUncontended-8 14.60 15.10 +3.42% BenchmarkChanContended 108.00 114.00 +5.56% BenchmarkChanContended-2 621.00 617.00 -0.64% BenchmarkChanContended-4 759.00 677.00 -10.80% BenchmarkChanContended-8 1635.00 1517.00 -7.22% BenchmarkChanSync 299.00 256.00 -14.38% BenchmarkChanSync-2 5055.00 4624.00 -8.53% BenchmarkChanSync-4 4998.00 4680.00 -6.36% BenchmarkChanSync-8 5019.00 4760.00 -5.16% BenchmarkChanProdCons0 316.00 274.00 -13.29% BenchmarkChanProdCons0-2 1280.00 617.00 -51.80% BenchmarkChanProdCons0-4 2433.00 1332.00 -45.25% BenchmarkChanProdCons0-8 3651.00 1934.00 -47.03% BenchmarkChanProdCons10 153.00 152.00 -0.65% BenchmarkChanProdCons10-2 626.00 581.00 -7.19% BenchmarkChanProdCons10-4 1440.00 1323.00 -8.12% BenchmarkChanProdCons10-8 2036.00 2017.00 -0.93% R=rsc, ken CC=golang-dev https://golang.org/cl/4790042
Diffstat (limited to 'src/pkg/runtime/chan.c')
-rw-r--r--src/pkg/runtime/chan.c127
1 files changed, 62 insertions, 65 deletions
diff --git a/src/pkg/runtime/chan.c b/src/pkg/runtime/chan.c
index bbe05e041c..926bde723c 100644
--- a/src/pkg/runtime/chan.c
+++ b/src/pkg/runtime/chan.c
@@ -21,7 +21,7 @@ struct SudoG
int16 offset; // offset of case number
int8 isfree; // offset of case number
SudoG* link;
- byte elem[8]; // synch data element (+ more)
+ byte* elem; // data element
};
struct WaitQ
@@ -38,8 +38,8 @@ struct Hchan
bool closed;
uint8 elemalign;
Alg* elemalg; // interface for element type
- uint32 sendx; // send index
- uint32 recvx; // receive index
+ uint32 sendx; // send index
+ uint32 recvx; // receive index
WaitQ recvq; // list of recv waiters
WaitQ sendq; // list of send waiters
SudoG* free; // freelist
@@ -170,6 +170,7 @@ void
runtime·chansend(Hchan *c, byte *ep, bool *pres)
{
SudoG *sg;
+ SudoG mysg;
G* gp;
if(c == nil)
@@ -185,7 +186,6 @@ runtime·chansend(Hchan *c, byte *ep, bool *pres)
}
runtime·lock(c);
-loop:
if(c->closed)
goto closed;
@@ -194,12 +194,12 @@ loop:
sg = dequeue(&c->recvq, c);
if(sg != nil) {
- if(ep != nil)
- c->elemalg->copy(c->elemsize, sg->elem, ep);
-
+ runtime·unlock(c);
+
gp = sg->g;
gp->param = sg;
- runtime·unlock(c);
+ if(sg->elem != nil)
+ c->elemalg->copy(c->elemsize, sg->elem, ep);
runtime·ready(gp);
if(pres != nil)
@@ -213,21 +213,22 @@ loop:
return;
}
- sg = allocsg(c);
- if(ep != nil)
- c->elemalg->copy(c->elemsize, sg->elem, ep);
+ mysg.elem = ep;
+ mysg.g = g;
+ mysg.selgen = g->selgen;
g->param = nil;
g->status = Gwaiting;
- enqueue(&c->sendq, sg);
+ enqueue(&c->sendq, &mysg);
runtime·unlock(c);
runtime·gosched();
- runtime·lock(c);
- sg = g->param;
- if(sg == nil)
- goto loop;
- freesg(c, sg);
- runtime·unlock(c);
+ if(g->param == nil) {
+ runtime·lock(c);
+ if(!c->closed)
+ runtime·throw("chansend: spurious wakeup");
+ goto closed;
+ }
+
return;
asynch:
@@ -240,17 +241,18 @@ asynch:
*pres = false;
return;
}
- sg = allocsg(c);
+ mysg.g = g;
+ mysg.elem = nil;
+ mysg.selgen = g->selgen;
g->status = Gwaiting;
- enqueue(&c->sendq, sg);
+ enqueue(&c->sendq, &mysg);
runtime·unlock(c);
runtime·gosched();
runtime·lock(c);
goto asynch;
}
- if(ep != nil)
- c->elemalg->copy(c->elemsize, chanbuf(c, c->sendx), ep);
+ c->elemalg->copy(c->elemsize, chanbuf(c, c->sendx), ep);
if(++c->sendx == c->dataqsiz)
c->sendx = 0;
c->qcount++;
@@ -258,7 +260,6 @@ asynch:
sg = dequeue(&c->recvq, c);
if(sg != nil) {
gp = sg->g;
- freesg(c, sg);
runtime·unlock(c);
runtime·ready(gp);
} else
@@ -277,6 +278,7 @@ void
runtime·chanrecv(Hchan* c, byte *ep, bool *selected, bool *received)
{
SudoG *sg;
+ SudoG mysg;
G *gp;
if(c == nil)
@@ -289,8 +291,6 @@ runtime·chanrecv(Hchan* c, byte *ep, bool *selected, bool *received)
runtime·printf("chanrecv: chan=%p\n", c);
runtime·lock(c);
-
-loop:
if(c->dataqsiz > 0)
goto asynch;
@@ -299,13 +299,12 @@ loop:
sg = dequeue(&c->sendq, c);
if(sg != nil) {
+ runtime·unlock(c);
+
if(ep != nil)
c->elemalg->copy(c->elemsize, ep, sg->elem);
- c->elemalg->copy(c->elemsize, sg->elem, nil);
-
gp = sg->g;
gp->param = sg;
- runtime·unlock(c);
runtime·ready(gp);
if(selected != nil)
@@ -321,25 +320,24 @@ loop:
return;
}
- sg = allocsg(c);
+ mysg.elem = ep;
+ mysg.g = g;
+ mysg.selgen = g->selgen;
g->param = nil;
g->status = Gwaiting;
- enqueue(&c->recvq, sg);
+ enqueue(&c->recvq, &mysg);
runtime·unlock(c);
runtime·gosched();
- runtime·lock(c);
- sg = g->param;
- if(sg == nil)
- goto loop;
+ if(g->param == nil) {
+ runtime·lock(c);
+ if(!c->closed)
+ runtime·throw("chanrecv: spurious wakeup");
+ goto closed;
+ }
- if(ep != nil)
- c->elemalg->copy(c->elemsize, ep, sg->elem);
- c->elemalg->copy(c->elemsize, sg->elem, nil);
if(received != nil)
*received = true;
- freesg(c, sg);
- runtime·unlock(c);
return;
asynch:
@@ -354,9 +352,11 @@ asynch:
*received = false;
return;
}
- sg = allocsg(c);
+ mysg.g = g;
+ mysg.elem = nil;
+ mysg.selgen = g->selgen;
g->status = Gwaiting;
- enqueue(&c->recvq, sg);
+ enqueue(&c->recvq, &mysg);
runtime·unlock(c);
runtime·gosched();
@@ -369,10 +369,10 @@ asynch:
if(++c->recvx == c->dataqsiz)
c->recvx = 0;
c->qcount--;
+
sg = dequeue(&c->sendq, c);
if(sg != nil) {
gp = sg->g;
- freesg(c, sg);
runtime·unlock(c);
runtime·ready(gp);
} else
@@ -721,7 +721,6 @@ selectrecv(Select *sel, Hchan *c, void *pc, void *elem, bool *received, int32 so
cas->so = so;
cas->kind = CaseRecv;
cas->u.recv.elemp = elem;
- cas->u.recv.receivedp = nil;
cas->u.recv.receivedp = received;
if(debug)
@@ -911,6 +910,7 @@ loop:
}
if(dfl != nil) {
+ selunlock(sel);
cas = dfl;
goto retc;
}
@@ -926,12 +926,12 @@ loop:
switch(cas->kind) {
case CaseRecv:
+ sg->elem = cas->u.recv.elemp;
enqueue(&c->recvq, sg);
break;
case CaseSend:
- if(c->dataqsiz == 0)
- c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem);
+ sg->elem = cas->u.elem;
enqueue(&c->sendq, sg);
break;
}
@@ -965,10 +965,8 @@ loop:
cas = sel->scase[o];
c = cas->chan;
- if(c->dataqsiz > 0) {
-// prints("shouldnt happen\n");
- goto loop;
- }
+ if(c->dataqsiz > 0)
+ runtime·throw("selectgo: shouldnt happen");
if(debug)
runtime·printf("wait-return: sel=%p c=%p cas=%p kind=%d o=%d\n",
@@ -977,12 +975,10 @@ loop:
if(cas->kind == CaseRecv) {
if(cas->u.recv.receivedp != nil)
*cas->u.recv.receivedp = true;
- if(cas->u.recv.elemp != nil)
- c->elemalg->copy(c->elemsize, cas->u.recv.elemp, sg->elem);
- c->elemalg->copy(c->elemsize, sg->elem, nil);
}
freesg(c, sg);
+ selunlock(sel);
goto retc;
asyncrecv:
@@ -998,35 +994,38 @@ asyncrecv:
sg = dequeue(&c->sendq, c);
if(sg != nil) {
gp = sg->g;
- freesg(c, sg);
+ selunlock(sel);
runtime·ready(gp);
+ } else {
+ selunlock(sel);
}
goto retc;
asyncsend:
// can send to buffer
- if(cas->u.elem != nil)
- c->elemalg->copy(c->elemsize, chanbuf(c, c->sendx), cas->u.elem);
+ c->elemalg->copy(c->elemsize, chanbuf(c, c->sendx), cas->u.elem);
if(++c->sendx == c->dataqsiz)
c->sendx = 0;
c->qcount++;
sg = dequeue(&c->recvq, c);
if(sg != nil) {
gp = sg->g;
- freesg(c, sg);
+ selunlock(sel);
runtime·ready(gp);
+ } else {
+ selunlock(sel);
}
goto retc;
syncrecv:
// can receive from sleeping sender (sg)
+ selunlock(sel);
if(debug)
runtime·printf("syncrecv: sel=%p c=%p o=%d\n", sel, c, o);
if(cas->u.recv.receivedp != nil)
*cas->u.recv.receivedp = true;
if(cas->u.recv.elemp != nil)
c->elemalg->copy(c->elemsize, cas->u.recv.elemp, sg->elem);
- c->elemalg->copy(c->elemsize, sg->elem, nil);
gp = sg->g;
gp->param = sg;
runtime·ready(gp);
@@ -1034,6 +1033,7 @@ syncrecv:
rclose:
// read at end of closed channel
+ selunlock(sel);
if(cas->u.recv.receivedp != nil)
*cas->u.recv.receivedp = false;
if(cas->u.recv.elemp != nil)
@@ -1042,18 +1042,16 @@ rclose:
syncsend:
// can send to sleeping receiver (sg)
+ selunlock(sel);
if(debug)
runtime·printf("syncsend: sel=%p c=%p o=%d\n", sel, c, o);
- if(c->closed)
- goto sclose;
- c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem);
+ if(sg->elem != nil)
+ c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem);
gp = sg->g;
gp->param = sg;
runtime·ready(gp);
retc:
- selunlock(sel);
-
// return to pc corresponding to chosen case
pc = cas->pc;
as = (byte*)selp + cas->so;
@@ -1093,7 +1091,6 @@ runtime·closechan(Hchan *c)
break;
gp = sg->g;
gp->param = nil;
- freesg(c, sg);
runtime·ready(gp);
}
@@ -1104,7 +1101,6 @@ runtime·closechan(Hchan *c)
break;
gp = sg->g;
gp->param = nil;
- freesg(c, sg);
runtime·ready(gp);
}
@@ -1203,7 +1199,7 @@ allocsg(Hchan *c)
if(sg != nil) {
c->free = sg->link;
} else
- sg = runtime·mal(sizeof(*sg) + c->elemsize - sizeof(sg->elem));
+ sg = runtime·mal(sizeof(*sg));
sg->selgen = g->selgen;
sg->g = g;
sg->offset = 0;
@@ -1220,6 +1216,7 @@ freesg(Hchan *c, SudoG *sg)
runtime·throw("chan.freesg: already free");
sg->isfree = 1;
sg->link = c->free;
+ sg->elem = nil;
c->free = sg;
}
}