cmd/gc: inline convT2E when T is uintptr-shaped.

GOARCH=amd64 benchmarks

src/pkg/runtime
benchmark                  old ns/op    new ns/op    delta
BenchmarkConvT2ESmall             10           10   +1.00%
BenchmarkConvT2EUintptr            9            0  -92.07%
BenchmarkConvT2EBig               74           74   -0.27%
BenchmarkConvT2I                  27           26   -3.62%
BenchmarkConvI2E                   4            4   -7.05%
BenchmarkConvI2I                  20           19   -2.99%

test/bench/go1
benchmark                 old ns/op    new ns/op    delta
BenchmarkBinaryTree17    5930908000   5937260000   +0.11%
BenchmarkFannkuch11      3927057000   3933556000   +0.17%
BenchmarkGobDecode         21998090     21870620   -0.58%
BenchmarkGobEncode         12725310     12734480   +0.07%
BenchmarkGzip             567617600    567892800   +0.05%
BenchmarkGunzip           178284100    178706900   +0.24%
BenchmarkJSONEncode        87693550     86794300   -1.03%
BenchmarkJSONDecode       314212600    324115000   +3.15%
BenchmarkMandelbrot200      7016640      7073766   +0.81%
BenchmarkParse              7852100      7892085   +0.51%
BenchmarkRevcomp         1285663000   1286147000   +0.04%
BenchmarkTemplate         566823800    567606200   +0.14%

I'm not entirely sure why the JSON* numbers have changed, but
eyeballing the profile suggests that it could be spending less
and more time in runtime.{new,old}stack, so it could simply be
stack-split boundary noise.

R=rsc, dave, bsiegert, dsymonds
CC=golang-dev
https://golang.org/cl/6280049
This commit is contained in:
Nigel Tao 2012-06-14 10:43:20 +10:00
parent 733ee91786
commit 8f84328fdc
8 changed files with 201 additions and 7 deletions

View file

@ -41,6 +41,14 @@ cgen(Node *n, Node *res)
} else
cgen_slice(n, res);
return;
case OEFACE:
if (res->op != ONAME || !res->addable) {
tempname(&n1, n->type);
cgen_eface(n, &n1);
cgen(&n1, res);
} else
cgen_eface(n, res);
return;
}
while(n->op == OCONVNOP)
@ -598,6 +606,12 @@ agen(Node *n, Node *res)
agen(&n1, res);
break;
case OEFACE:
tempname(&n1, n->type);
cgen_eface(n, &n1);
agen(&n1, res);
break;
case OINDEX:
p2 = nil; // to be patched to panicindex.
w = n->type->width;

View file

@ -44,6 +44,14 @@ cgen(Node *n, Node *res)
} else
cgen_slice(n, res);
goto ret;
case OEFACE:
if (res->op != ONAME || !res->addable) {
tempname(&n1, n->type);
cgen_eface(n, &n1);
cgen(&n1, res);
} else
cgen_eface(n, res);
goto ret;
}
if(n->ullman >= UINF) {
@ -549,6 +557,12 @@ agen(Node *n, Node *res)
agen(&n1, res);
break;
case OEFACE:
tempname(&n1, n->type);
cgen_eface(n, &n1);
agen(&n1, res);
break;
case OINDEX:
w = n->type->width;
if(nr->addable)

View file

@ -74,6 +74,14 @@ cgen(Node *n, Node *res)
} else
cgen_slice(n, res);
return;
case OEFACE:
if (res->op != ONAME || !res->addable) {
tempname(&n1, n->type);
cgen_eface(n, &n1);
cgen(&n1, res);
} else
cgen_eface(n, res);
return;
}
while(n->op == OCONVNOP)
@ -549,6 +557,12 @@ agen(Node *n, Node *res)
agen(&n1, res);
break;
case OEFACE:
tempname(&n1, n->type);
cgen_eface(n, &n1);
agen(&n1, res);
break;
case OINDEX:
p2 = nil; // to be patched to panicindex.
w = n->type->width;

View file

@ -737,6 +737,22 @@ ret:
;
}
/*
* generate:
* res = iface{typ, data}
* n->left is typ
* n->right is data
*/
void
cgen_eface(Node *n, Node *res)
{
Node dst;
dst = *res;
dst.type = types[tptr];
cgen(n->left, &dst);
dst.xoffset += widthptr;
cgen(n->right, &dst);
}
/*
* generate:
@ -744,15 +760,12 @@ ret:
* n->left is s
* n->list is (cap(s)-lo(TUINT32), hi-lo(TUINT32)[, lo*width(TUINTPTR)])
* caller (cgen) guarantees res is an addable ONAME.
*
*/
void
cgen_slice(Node *n, Node *res)
{
Node src, dst, *cap, *len, *offs, *add;
// print("cgen_slice: %N = %+N\n", res, n);
cap = n->list->n;
len = n->list->next->n;
offs = N;

View file

@ -488,6 +488,7 @@ enum
ODDD,
ODDDARG,
OINLCALL, // intermediary representation of an inlined call
OEFACE, // itable and data words of empty-interface value
OITAB, // itable word of interface value
// for back ends
@ -989,6 +990,7 @@ void dumplist(char *s, NodeList *l);
void addrescapes(Node *n);
void cgen_as(Node *nl, Node *nr);
void cgen_callmeth(Node *n, int proc);
void cgen_eface(Node* n, Node* res);
void cgen_slice(Node* n, Node* res);
void clearlabels(void);
void checklabels(void);

View file

@ -436,6 +436,11 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&n->left, init);
goto ret;
case OEFACE:
walkexpr(&n->left, init);
walkexpr(&n->right, init);
goto ret;
case OITAB:
walkexpr(&n->left, init);
goto ret;
@ -713,10 +718,22 @@ walkexpr(Node **np, NodeList **init)
goto ret;
case OCONVIFACE:
walkexpr(&n->left, init);
// Optimize convT2E as a two-word copy when T is uintptr-shaped.
if(!isinter(n->left->type) && isnilinter(n->type) &&
(n->left->type->width == widthptr) &&
isint[simsimtype(n->left->type)]) {
l = nod(OEFACE, typename(n->left->type), n->left);
l->type = n->type;
l->typecheck = n->typecheck;
n = l;
goto ret;
}
// Build name of function: convI2E etc.
// Not all names are possible
// (e.g., we'll never generate convE2E or convE2I).
walkexpr(&n->left, init);
strcpy(buf, "conv");
p = buf+strlen(buf);
if(isnilinter(n->left->type))

View file

@ -20,14 +20,20 @@ var (
Big [2]*int
)
func BenchmarkConvT2E(b *testing.B) {
func BenchmarkConvT2ESmall(b *testing.B) {
for i := 0; i < b.N; i++ {
I = 1
I = uint16(1)
}
}
func BenchmarkConvT2EUintptr(b *testing.B) {
for i := 0; i < b.N; i++ {
I = uintptr(1)
}
}
func BenchmarkConvT2EBig(b *testing.B) {
v := [2]*int{}
v := [2]uintptr{1, 2}
for i := 0; i < b.N; i++ {
I = v
}

114
test/convT2E.go Normal file
View file

@ -0,0 +1,114 @@
// run
// 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.
// Test conversion from non-interface types to the empty interface.
package main
var (
z = struct{}{}
p = &z
pp = &p
u16 = uint16(1)
u32 = uint32(2)
u64 = uint64(3)
u128 = [2]uint64{4, 5}
f32 = float32(6)
f64 = float64(7)
c128 = complex128(8 + 9i)
s = "10"
b = []byte("11")
m = map[int]int{12: 13}
c = make(chan int, 14)
)
var (
iz interface{} = z
ip interface{} = p
ipp interface{} = pp
iu16 interface{} = u16
iu32 interface{} = u32
iu64 interface{} = u64
iu128 interface{} = u128
if32 interface{} = f32
if64 interface{} = f64
ic128 interface{} = c128
is interface{} = s
ib interface{} = b
im interface{} = m
ic interface{} = c
)
func second(a ...interface{}) interface{} {
return a[1]
}
func main() {
// Test equality. There are no tests for b and m, as slices and
// maps are not comparable by ==.
if z != iz {
panic("z != iz")
}
if p != ip {
panic("p != ip")
}
if pp != ipp {
panic("pp != ipp")
}
if u16 != iu16 {
panic("u16 != iu16")
}
if u32 != iu32 {
panic("u32 != iu32")
}
if u64 != iu64 {
panic("u64 != iu64")
}
if u128 != iu128 {
panic("u128 != iu128")
}
if f32 != if32 {
panic("f32 != if32")
}
if f64 != if64 {
panic("f64 != if64")
}
if c128 != ic128 {
panic("c128 != ic128")
}
if s != is {
panic("s != is")
}
if c != ic {
panic("c != ic")
}
// Test that non-interface types can be used as ...interface{} arguments.
if got := second(z, p, pp, u16, u32, u64, u128, f32, f64, c128, s, b, m, c); got != ip {
println("second: got", got, "want", ip)
panic("fail")
}
// Test that non-interface types can be sent on a chan interface{}.
const n = 100
uc := make(chan interface{})
go func() {
for i := 0; i < n; i++ {
select {
case uc <- nil:
case uc <- u32:
case uc <- u64:
case uc <- u128:
}
}
}()
for i := 0; i < n; i++ {
if got := <-uc; got != nil && got != u32 && got != u64 && got != u128 {
println("recv: i", i, "got", got)
panic("fail")
}
}
}