From f91cc3bdbbdad81a4d2be5833a6a1816355d93e1 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sat, 11 Feb 2012 00:19:24 -0500 Subject: [PATCH] gc: optimize interface ==, != If the values being compared have different concrete types, then they're clearly unequal without needing to invoke the actual interface compare routine. This speeds tests for specific values, like if err == io.EOF, by about 3x. benchmark old ns/op new ns/op delta BenchmarkIfaceCmp100 843 287 -65.95% BenchmarkIfaceCmpNil100 184 182 -1.09% Fixes #2591. R=ken2 CC=golang-dev https://golang.org/cl/5651073 --- src/cmd/5g/cgen.c | 17 ++++++++++++++ src/cmd/5g/gsubr.c | 21 +++++++++++++++-- src/cmd/6g/cgen.c | 11 +++++++++ src/cmd/6g/gsubr.c | 12 ++++++++++ src/cmd/8g/cgen.c | 10 +++++++++ src/cmd/8g/gsubr.c | 12 ++++++++++ src/cmd/gc/go.h | 1 + src/cmd/gc/typecheck.c | 10 +++++++++ src/cmd/gc/walk.c | 21 ++++++++++++++--- src/pkg/runtime/runtime_test.go | 40 +++++++++++++++++++++++++++++++++ 10 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 src/pkg/runtime/runtime_test.go diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c index 4912dcd99c..cccef94c94 100644 --- a/src/cmd/5g/cgen.c +++ b/src/cmd/5g/cgen.c @@ -64,6 +64,9 @@ cgen(Node *n, Node *res) if(isslice(n->left->type)) n->addable = n->left->addable; break; + case OITAB: + n->addable = n->left->addable; + break; } // if both are addressable, move @@ -280,6 +283,20 @@ cgen(Node *n, Node *res) regfree(&n1); break; + case OITAB: + // itable of interface value + igen(nl, &n1, res); + n1.op = OREGISTER; // was OINDREG + regalloc(&n2, n->type, &n1); + n1.op = OINDREG; + n1.type = n->type; + n1.xoffset = 0; + gmove(&n1, &n2); + gmove(&n2, res); + regfree(&n1); + regfree(&n2); + break; + case OLEN: if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { // map has len in the first 32-bit word. diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index d8460ff754..94caeb0918 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -346,6 +346,8 @@ anyregalloc(void) return 0; } +uintptr regpc[REGALLOC_RMAX+1]; + /* * allocate register of type t, leave in n. * if o != N, o is desired fixed register. @@ -389,9 +391,12 @@ regalloc(Node *n, Type *t, Node *o) goto out; } for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++) - if(reg[i] == 0) + if(reg[i] == 0) { + regpc[i] = (uintptr)getcallerpc(&n); goto out; - + } + for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++) + print("%d %p\n", i, regpc[i]); yyerror("out of fixed registers"); goto err; @@ -451,6 +456,8 @@ regfree(Node *n) if(reg[i] <= 0) fatal("regfree: reg not allocated"); reg[i]--; + if(reg[i] == 0) + regpc[i] = 0; } /* @@ -1347,6 +1354,16 @@ naddr(Node *n, Addr *a, int canemitcode) } break; + case OITAB: + // itable of interface value + naddr(n->left, a, canemitcode); + a->etype = TINT32; + if(a->type == D_CONST && a->offset == 0) + break; // len(nil) + if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) + checkoffset(a, canemitcode); + break; + case OLEN: // len of string or slice naddr(n->left, a, canemitcode); diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c index 2521b02d23..00334e71b1 100644 --- a/src/cmd/6g/cgen.c +++ b/src/cmd/6g/cgen.c @@ -125,6 +125,9 @@ cgen(Node *n, Node *res) if(isslice(n->left->type)) n->addable = n->left->addable; break; + case OITAB: + n->addable = n->left->addable; + break; } if(complexop(n, res)) { @@ -259,6 +262,14 @@ cgen(Node *n, Node *res) gmove(&n1, res); regfree(&n1); break; + + case OITAB: + // interface table is first word of interface value + igen(nl, &n1, res); + n1.type = n->type; + gmove(&n1, res); + regfree(&n1); + break; case OLEN: if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index 22fea9b166..02df69ac3c 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -563,6 +563,7 @@ int ismem(Node *n) { switch(n->op) { + case OITAB: case OLEN: case OCAP: case OINDREG: @@ -1219,6 +1220,17 @@ naddr(Node *n, Addr *a, int canemitcode) break; } fatal("naddr: OADDR\n"); + + case OITAB: + // itable of interface value + naddr(n->left, a, canemitcode); + if(a->type == D_CONST && a->offset == 0) + break; // itab(nil) + a->etype = tptr; + a->width = widthptr; + if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) + checkoffset(a, canemitcode); + break; case OLEN: // len of string or slice diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c index 7dd3a7bb12..5d8be4678b 100644 --- a/src/cmd/8g/cgen.c +++ b/src/cmd/8g/cgen.c @@ -98,6 +98,9 @@ cgen(Node *n, Node *res) if(isslice(n->left->type)) n->addable = n->left->addable; break; + case OITAB: + n->addable = n->left->addable; + break; } // if both are addressable, move @@ -252,6 +255,13 @@ cgen(Node *n, Node *res) regfree(&n1); break; + case OITAB: + igen(nl, &n1, res); + n1.type = ptrto(types[TUINTPTR]); + gmove(&n1, res); + regfree(&n1); + break; + case OLEN: if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { // map has len in the first 32-bit word. diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index 44dcd50b54..dd35c51b08 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -1041,6 +1041,7 @@ int ismem(Node *n) { switch(n->op) { + case OITAB: case OLEN: case OCAP: case OINDREG: @@ -1926,6 +1927,17 @@ naddr(Node *n, Addr *a, int canemitcode) break; } fatal("naddr: OADDR\n"); + + case OITAB: + // itable of interface value + naddr(n->left, a, canemitcode); + if(a->type == D_CONST && a->offset == 0) + break; // len(nil) + a->etype = tptr; + a->width = widthptr; + if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) + checkoffset(a, canemitcode); + break; case OLEN: // len of string or slice diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 6b0709c16e..0fde506577 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -484,6 +484,7 @@ enum ODDD, ODDDARG, OINLCALL, // intermediary representation of an inlined call + OITAB, // itable word of interface value // for back ends OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG, diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index edd6b729d0..91f545849a 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -1304,6 +1304,16 @@ reswitch: if(n->type == T) goto error; goto ret; + + case OITAB: + ok |= Erv; + typecheck(&n->left, Erv); + if((t = n->left->type) == T) + goto error; + if(t->etype != TINTER) + fatal("OITAB of %T", t); + n->type = ptrto(types[TUINTPTR]); + goto ret; /* * statements diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index 37691f029f..0118c08a74 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -432,6 +432,10 @@ walkexpr(Node **np, NodeList **init) walkexpr(&n->left, init); goto ret; + case OITAB: + walkexpr(&n->left, init); + goto ret; + case OLEN: case OCAP: walkexpr(&n->left, init); @@ -1176,10 +1180,21 @@ walkexpr(Node **np, NodeList **init) argtype(fn, n->right->type); argtype(fn, n->left->type); r = mkcall1(fn, n->type, init, n->left, n->right); - if(n->etype == ONE) { + if(n->etype == ONE) r = nod(ONOT, r, N); - typecheck(&r, Erv); - } + + // check itable/type before full compare. + if(n->etype == OEQ) + r = nod(OANDAND, nod(OEQ, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r); + else + r = nod(OOROR, nod(ONE, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r); + typecheck(&r, Erv); + walkexpr(&r, nil); + + n = r; + goto ret; + + n = r; goto ret; diff --git a/src/pkg/runtime/runtime_test.go b/src/pkg/runtime/runtime_test.go new file mode 100644 index 0000000000..d68b363e99 --- /dev/null +++ b/src/pkg/runtime/runtime_test.go @@ -0,0 +1,40 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime_test + +import ( + "io" + "testing" +) + +var errf error + +func errfn() error { + return errf +} + +func errfn1() error { + return io.EOF +} + +func BenchmarkIfaceCmp100(b *testing.B) { + for i := 0; i < b.N; i++ { + for j := 0; j < 100; j++ { + if errfn() == io.EOF { + b.Fatal("bad comparison") + } + } + } +} + +func BenchmarkIfaceCmpNil100(b *testing.B) { + for i := 0; i < b.N; i++ { + for j := 0; j < 100; j++ { + if errfn1() == nil { + b.Fatal("bad comparison") + } + } + } +} -- 2.50.0