1
0
mirror of https://github.com/golang/go synced 2024-11-12 06:20:22 -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:
Russ Cox 2012-02-11 00:19:24 -05:00
parent a7b83f2287
commit f91cc3bdbb
10 changed files with 150 additions and 5 deletions

View File

@ -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.

View File

@ -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);

View File

@ -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)) {

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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;

View 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")
}
}
}
}