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
if(isslice(n->left->type))
n->addable = n->left->addable;
break;
+ case OITAB:
+ n->addable = n->left->addable;
+ break;
}
// if both are addressable, move
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.
return 0;
}
+uintptr regpc[REGALLOC_RMAX+1];
+
/*
* allocate register of type t, leave in n.
* if o != N, o is desired fixed register.
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;
if(reg[i] <= 0)
fatal("regfree: reg not allocated");
reg[i]--;
+ if(reg[i] == 0)
+ regpc[i] = 0;
}
/*
}
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);
if(isslice(n->left->type))
n->addable = n->left->addable;
break;
+ case OITAB:
+ n->addable = n->left->addable;
+ break;
}
if(complexop(n, 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)) {
ismem(Node *n)
{
switch(n->op) {
+ case OITAB:
case OLEN:
case OCAP:
case OINDREG:
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
if(isslice(n->left->type))
n->addable = n->left->addable;
break;
+ case OITAB:
+ n->addable = n->left->addable;
+ break;
}
// if both are addressable, move
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.
ismem(Node *n)
{
switch(n->op) {
+ case OITAB:
case OLEN:
case OCAP:
case OINDREG:
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
ODDD,
ODDDARG,
OINLCALL, // intermediary representation of an inlined call
+ OITAB, // itable word of interface value
// for back ends
OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG,
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
walkexpr(&n->left, init);
goto ret;
+ case OITAB:
+ walkexpr(&n->left, init);
+ goto ret;
+
case OLEN:
case OCAP:
walkexpr(&n->left, 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;
--- /dev/null
+// 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")
+ }
+ }
+ }
+}