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:
parent
a7b83f2287
commit
f91cc3bdbb
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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)) {
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
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