mirror of
https://github.com/golang/go
synced 2024-11-22 08:54:39 -07:00
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
This commit is contained in:
parent
a7b83f2287
commit
f91cc3bdbb
@ -64,6 +64,9 @@ cgen(Node *n, Node *res)
|
|||||||
if(isslice(n->left->type))
|
if(isslice(n->left->type))
|
||||||
n->addable = n->left->addable;
|
n->addable = n->left->addable;
|
||||||
break;
|
break;
|
||||||
|
case OITAB:
|
||||||
|
n->addable = n->left->addable;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if both are addressable, move
|
// if both are addressable, move
|
||||||
@ -280,6 +283,20 @@ cgen(Node *n, Node *res)
|
|||||||
regfree(&n1);
|
regfree(&n1);
|
||||||
break;
|
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:
|
case OLEN:
|
||||||
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
|
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
|
||||||
// map has len in the first 32-bit word.
|
// map has len in the first 32-bit word.
|
||||||
|
@ -346,6 +346,8 @@ anyregalloc(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uintptr regpc[REGALLOC_RMAX+1];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* allocate register of type t, leave in n.
|
* allocate register of type t, leave in n.
|
||||||
* if o != N, o is desired fixed register.
|
* if o != N, o is desired fixed register.
|
||||||
@ -389,9 +391,12 @@ regalloc(Node *n, Type *t, Node *o)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
|
for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
|
||||||
if(reg[i] == 0)
|
if(reg[i] == 0) {
|
||||||
|
regpc[i] = (uintptr)getcallerpc(&n);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
|
||||||
|
print("%d %p\n", i, regpc[i]);
|
||||||
yyerror("out of fixed registers");
|
yyerror("out of fixed registers");
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@ -451,6 +456,8 @@ regfree(Node *n)
|
|||||||
if(reg[i] <= 0)
|
if(reg[i] <= 0)
|
||||||
fatal("regfree: reg not allocated");
|
fatal("regfree: reg not allocated");
|
||||||
reg[i]--;
|
reg[i]--;
|
||||||
|
if(reg[i] == 0)
|
||||||
|
regpc[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1347,6 +1354,16 @@ naddr(Node *n, Addr *a, int canemitcode)
|
|||||||
}
|
}
|
||||||
break;
|
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:
|
case OLEN:
|
||||||
// len of string or slice
|
// len of string or slice
|
||||||
naddr(n->left, a, canemitcode);
|
naddr(n->left, a, canemitcode);
|
||||||
|
@ -125,6 +125,9 @@ cgen(Node *n, Node *res)
|
|||||||
if(isslice(n->left->type))
|
if(isslice(n->left->type))
|
||||||
n->addable = n->left->addable;
|
n->addable = n->left->addable;
|
||||||
break;
|
break;
|
||||||
|
case OITAB:
|
||||||
|
n->addable = n->left->addable;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(complexop(n, res)) {
|
if(complexop(n, res)) {
|
||||||
@ -260,6 +263,14 @@ cgen(Node *n, Node *res)
|
|||||||
regfree(&n1);
|
regfree(&n1);
|
||||||
break;
|
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:
|
case OLEN:
|
||||||
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
|
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
|
||||||
// map and chan have len in the first 32-bit word.
|
// map and chan have len in the first 32-bit word.
|
||||||
|
@ -563,6 +563,7 @@ int
|
|||||||
ismem(Node *n)
|
ismem(Node *n)
|
||||||
{
|
{
|
||||||
switch(n->op) {
|
switch(n->op) {
|
||||||
|
case OITAB:
|
||||||
case OLEN:
|
case OLEN:
|
||||||
case OCAP:
|
case OCAP:
|
||||||
case OINDREG:
|
case OINDREG:
|
||||||
@ -1220,6 +1221,17 @@ naddr(Node *n, Addr *a, int canemitcode)
|
|||||||
}
|
}
|
||||||
fatal("naddr: OADDR\n");
|
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:
|
case OLEN:
|
||||||
// len of string or slice
|
// len of string or slice
|
||||||
naddr(n->left, a, canemitcode);
|
naddr(n->left, a, canemitcode);
|
||||||
|
@ -98,6 +98,9 @@ cgen(Node *n, Node *res)
|
|||||||
if(isslice(n->left->type))
|
if(isslice(n->left->type))
|
||||||
n->addable = n->left->addable;
|
n->addable = n->left->addable;
|
||||||
break;
|
break;
|
||||||
|
case OITAB:
|
||||||
|
n->addable = n->left->addable;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if both are addressable, move
|
// if both are addressable, move
|
||||||
@ -252,6 +255,13 @@ cgen(Node *n, Node *res)
|
|||||||
regfree(&n1);
|
regfree(&n1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OITAB:
|
||||||
|
igen(nl, &n1, res);
|
||||||
|
n1.type = ptrto(types[TUINTPTR]);
|
||||||
|
gmove(&n1, res);
|
||||||
|
regfree(&n1);
|
||||||
|
break;
|
||||||
|
|
||||||
case OLEN:
|
case OLEN:
|
||||||
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
|
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
|
||||||
// map has len in the first 32-bit word.
|
// map has len in the first 32-bit word.
|
||||||
|
@ -1041,6 +1041,7 @@ int
|
|||||||
ismem(Node *n)
|
ismem(Node *n)
|
||||||
{
|
{
|
||||||
switch(n->op) {
|
switch(n->op) {
|
||||||
|
case OITAB:
|
||||||
case OLEN:
|
case OLEN:
|
||||||
case OCAP:
|
case OCAP:
|
||||||
case OINDREG:
|
case OINDREG:
|
||||||
@ -1927,6 +1928,17 @@ naddr(Node *n, Addr *a, int canemitcode)
|
|||||||
}
|
}
|
||||||
fatal("naddr: OADDR\n");
|
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:
|
case OLEN:
|
||||||
// len of string or slice
|
// len of string or slice
|
||||||
naddr(n->left, a, canemitcode);
|
naddr(n->left, a, canemitcode);
|
||||||
|
@ -484,6 +484,7 @@ enum
|
|||||||
ODDD,
|
ODDD,
|
||||||
ODDDARG,
|
ODDDARG,
|
||||||
OINLCALL, // intermediary representation of an inlined call
|
OINLCALL, // intermediary representation of an inlined call
|
||||||
|
OITAB, // itable word of interface value
|
||||||
|
|
||||||
// for back ends
|
// for back ends
|
||||||
OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG,
|
OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG,
|
||||||
|
@ -1305,6 +1305,16 @@ reswitch:
|
|||||||
goto error;
|
goto error;
|
||||||
goto ret;
|
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
|
* statements
|
||||||
*/
|
*/
|
||||||
|
@ -432,6 +432,10 @@ walkexpr(Node **np, NodeList **init)
|
|||||||
walkexpr(&n->left, init);
|
walkexpr(&n->left, init);
|
||||||
goto ret;
|
goto ret;
|
||||||
|
|
||||||
|
case OITAB:
|
||||||
|
walkexpr(&n->left, init);
|
||||||
|
goto ret;
|
||||||
|
|
||||||
case OLEN:
|
case OLEN:
|
||||||
case OCAP:
|
case OCAP:
|
||||||
walkexpr(&n->left, init);
|
walkexpr(&n->left, init);
|
||||||
@ -1176,10 +1180,21 @@ walkexpr(Node **np, NodeList **init)
|
|||||||
argtype(fn, n->right->type);
|
argtype(fn, n->right->type);
|
||||||
argtype(fn, n->left->type);
|
argtype(fn, n->left->type);
|
||||||
r = mkcall1(fn, n->type, init, n->left, n->right);
|
r = mkcall1(fn, n->type, init, n->left, n->right);
|
||||||
if(n->etype == ONE) {
|
if(n->etype == ONE)
|
||||||
r = nod(ONOT, r, N);
|
r = nod(ONOT, r, N);
|
||||||
|
|
||||||
|
// 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);
|
typecheck(&r, Erv);
|
||||||
}
|
walkexpr(&r, nil);
|
||||||
|
|
||||||
|
n = r;
|
||||||
|
goto ret;
|
||||||
|
|
||||||
|
|
||||||
n = r;
|
n = r;
|
||||||
goto ret;
|
goto ret;
|
||||||
|
|
||||||
|
40
src/pkg/runtime/runtime_test.go
Normal file
40
src/pkg/runtime/runtime_test.go
Normal file
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user