Commit 18e86644 authored by Nigel Tao's avatar Nigel Tao

cmd/gc: cache itab lookup in convT2I.

There may be further savings if convT2I can avoid the function call
if the cache is good and T is uintptr-shaped, a la convT2E, but that
will be a follow-up CL.

src/pkg/runtime:
benchmark                  old ns/op    new ns/op    delta
BenchmarkConvT2ISmall             43           15  -64.01%
BenchmarkConvT2IUintptr           45           14  -67.48%
BenchmarkConvT2ILarge            130          101  -22.31%

test/bench/go1:
benchmark                 old ns/op    new ns/op    delta
BenchmarkBinaryTree17    8588997000   8499058000   -1.05%
BenchmarkFannkuch11      5300392000   5358093000   +1.09%
BenchmarkGobDecode         30295580     31040190   +2.46%
BenchmarkGobEncode         18102070     17675650   -2.36%
BenchmarkGzip             774191400    771591400   -0.34%
BenchmarkGunzip           245915100    247464100   +0.63%
BenchmarkJSONEncode       123577000    121423050   -1.74%
BenchmarkJSONDecode       451969800    596256200  +31.92%
BenchmarkMandelbrot200     10060050     10072880   +0.13%
BenchmarkParse             10989840     11037710   +0.44%
BenchmarkRevcomp         1782666000   1716864000   -3.69%
BenchmarkTemplate         798286600    723234400   -9.40%

R=rsc, bradfitz, go.peter.90, daniel.morsing, dave, uriel
CC=golang-dev
https://golang.org/cl/6337058
parent 917f7643
......@@ -208,7 +208,7 @@ ggloblnod(Node *nam, int32 width)
}
void
ggloblsym(Sym *s, int32 width, int dupok)
ggloblsym(Sym *s, int32 width, int dupok, int rodata)
{
Prog *p;
......@@ -220,8 +220,9 @@ ggloblsym(Sym *s, int32 width, int dupok)
p->to.name = D_NONE;
p->to.offset = width;
if(dupok)
p->reg = DUPOK;
p->reg |= RODATA;
p->reg |= DUPOK;
if(rodata)
p->reg |= RODATA;
}
int
......
......@@ -206,7 +206,7 @@ ggloblnod(Node *nam, int32 width)
}
void
ggloblsym(Sym *s, int32 width, int dupok)
ggloblsym(Sym *s, int32 width, int dupok, int rodata)
{
Prog *p;
......@@ -218,8 +218,9 @@ ggloblsym(Sym *s, int32 width, int dupok)
p->to.index = D_NONE;
p->to.offset = width;
if(dupok)
p->from.scale = DUPOK;
p->from.scale |= RODATA;
p->from.scale |= DUPOK;
if(rodata)
p->from.scale |= RODATA;
}
int
......
......@@ -207,7 +207,7 @@ ggloblnod(Node *nam, int32 width)
}
void
ggloblsym(Sym *s, int32 width, int dupok)
ggloblsym(Sym *s, int32 width, int dupok, int rodata)
{
Prog *p;
......@@ -219,8 +219,9 @@ ggloblsym(Sym *s, int32 width, int dupok)
p->to.index = D_NONE;
p->to.offset = width;
if(dupok)
p->from.scale = DUPOK;
p->from.scale |= RODATA;
p->from.scale |= DUPOK;
if(rodata)
p->from.scale |= RODATA;
}
int
......
......@@ -41,7 +41,7 @@ char *runtimeimport =
"func @\"\".convI2E(@\"\".elem any) (@\"\".ret any)\n"
"func @\"\".convI2I(@\"\".typ *byte, @\"\".elem any) (@\"\".ret any)\n"
"func @\"\".convT2E(@\"\".typ *byte, @\"\".elem any) (@\"\".ret any)\n"
"func @\"\".convT2I(@\"\".typ *byte, @\"\".typ2 *byte, @\"\".elem any) (@\"\".ret any)\n"
"func @\"\".convT2I(@\"\".typ *byte, @\"\".typ2 *byte, @\"\".cache **byte, @\"\".elem any) (@\"\".ret any)\n"
"func @\"\".assertE2E(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n"
"func @\"\".assertE2E2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n"
"func @\"\".assertE2I(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n"
......
......@@ -767,6 +767,7 @@ EXTERN Pkg* importpkg; // package being imported
EXTERN Pkg* structpkg; // package that declared struct, during import
EXTERN Pkg* builtinpkg; // fake package for builtins
EXTERN Pkg* gostringpkg; // fake pkg for Go strings
EXTERN Pkg* itabpkg; // fake pkg for itab cache
EXTERN Pkg* runtimepkg; // package runtime
EXTERN Pkg* stringpkg; // fake package for C strings
EXTERN Pkg* typepkg; // fake package for runtime type info
......@@ -1330,7 +1331,7 @@ void gdatacomplex(Node*, Mpcplx*);
void gdatastring(Node*, Strlit*);
void genembedtramp(Type*, Type*, Sym*, int iface);
void ggloblnod(Node *nam, int32 width);
void ggloblsym(Sym *s, int32 width, int dupok);
void ggloblsym(Sym *s, int32 width, int dupok, int rodata);
Prog* gjmp(Prog*);
void gused(Node*);
int isfat(Type*);
......
......@@ -204,6 +204,10 @@ main(int argc, char *argv[])
gostringpkg->name = "go.string";
gostringpkg->prefix = "go.string"; // not go%2estring
itabpkg = mkpkg(strlit("go.itab"));
itabpkg->name = "go.itab";
itabpkg->prefix = "go.itab"; // not go%2eitab
runtimepkg = mkpkg(strlit("runtime"));
runtimepkg->name = "runtime";
......
......@@ -314,7 +314,7 @@ stringsym(char *s, int len)
}
off = duint8(sym, off, 0); // terminating NUL for runtime
off = (off+widthptr-1)&~(widthptr-1); // round to pointer alignment
ggloblsym(sym, off, 1);
ggloblsym(sym, off, 1, 1);
return sym;
}
......@@ -305,7 +305,7 @@ dimportpath(Pkg *p)
p->pathsym = n->sym;
gdatastring(n, p->path);
ggloblsym(n->sym, types[TSTRING]->width, 1);
ggloblsym(n->sym, types[TSTRING]->width, 1, 1);
}
static int
......@@ -857,7 +857,7 @@ ok:
break;
}
ot = dextratype(s, ot, t, xt);
ggloblsym(s, ot, dupok);
ggloblsym(s, ot, dupok, 1);
return s;
}
......@@ -955,7 +955,7 @@ dalgsym(Type *t)
break;
}
ggloblsym(s, ot, 1);
ggloblsym(s, ot, 1, 1);
return s;
}
......@@ -61,7 +61,7 @@ func slicestringcopy(to any, fr any) int
func convI2E(elem any) (ret any)
func convI2I(typ *byte, elem any) (ret any)
func convT2E(typ *byte, elem any) (ret any)
func convT2I(typ *byte, typ2 *byte, elem any) (ret any)
func convT2I(typ *byte, typ2 *byte, cache **byte, elem any) (ret any)
// interface type assertions x.(T)
func assertE2E(typ *byte, iface any) (ret any)
......
......@@ -375,6 +375,7 @@ walkexpr(Node **np, NodeList **init)
int64 v;
int32 lno;
Node *n, *fn;
Sym *sym;
char buf[100], *p;
n = *np;
......@@ -755,6 +756,22 @@ walkexpr(Node **np, NodeList **init)
ll = list(ll, typename(n->left->type));
if(!isnilinter(n->type))
ll = list(ll, typename(n->type));
if(!isinter(n->left->type) && !isnilinter(n->type)){
sym = pkglookup(smprint("%-T.%-T", n->left->type, n->type), itabpkg);
if(sym->def == N) {
l = nod(ONAME, N, N);
l->sym = sym;
l->type = ptrto(types[TUINT8]);
l->addable = 1;
l->class = PEXTERN;
l->xoffset = 0;
sym->def = l;
ggloblsym(sym, widthptr, 1, 0);
}
l = nod(OADDR, sym->def, N);
l->addable = 1;
ll = list(ll, l);
}
ll = list(ll, n->left);
argtype(fn, n->left->type);
argtype(fn, n->type);
......
......@@ -183,19 +183,25 @@ copyout(Type *t, void **src, void *dst)
alg->copy(size, dst, *src);
}
// func convT2I(typ *byte, typ2 *byte, elem any) (ret any)
// func convT2I(typ *byte, typ2 *byte, cache **byte, elem any) (ret any)
#pragma textflag 7
void
runtime·convT2I(Type *t, InterfaceType *inter, ...)
runtime·convT2I(Type *t, InterfaceType *inter, Itab **cache, ...)
{
byte *elem;
Iface *ret;
Itab *tab;
int32 wid;
elem = (byte*)(&inter+1);
elem = (byte*)(&cache+1);
wid = t->size;
ret = (Iface*)(elem + ROUND(wid, Structrnd));
ret->tab = itab(inter, t, 0);
tab = runtime·atomicloadp(cache);
if(!tab) {
tab = itab(inter, t, 0);
runtime·atomicstorep(cache, tab);
}
ret->tab = tab;
copyin(t, elem, &ret->data);
}
......
......@@ -5,98 +5,134 @@
package runtime_test
import (
"bytes"
"io"
"testing"
)
type I1 interface {
Method1()
}
type I2 interface {
Method1()
Method2()
}
type TS uint16
type TM uintptr
type TL [2]uintptr
func (TS) Method1() {}
func (TS) Method2() {}
func (TM) Method1() {}
func (TM) Method2() {}
func (TL) Method1() {}
func (TL) Method2() {}
var (
I interface{}
J int
B = new(bytes.Buffer)
W io.Writer = B
I2 interface{} = B
R io.ReadWriter = B
Big [2]*int
e interface{}
e_ interface{}
i1 I1
i2 I2
ts TS
tm TM
tl TL
)
func BenchmarkConvT2ESmall(b *testing.B) {
for i := 0; i < b.N; i++ {
I = uint16(1)
e = ts
}
}
func BenchmarkConvT2EUintptr(b *testing.B) {
for i := 0; i < b.N; i++ {
I = uintptr(1)
e = tm
}
}
func BenchmarkConvT2ELarge(b *testing.B) {
for i := 0; i < b.N; i++ {
e = tl
}
}
func BenchmarkConvT2ISmall(b *testing.B) {
for i := 0; i < b.N; i++ {
i1 = ts
}
}
func BenchmarkConvT2EBig(b *testing.B) {
v := [2]uintptr{1, 2}
func BenchmarkConvT2IUintptr(b *testing.B) {
for i := 0; i < b.N; i++ {
I = v
i1 = tm
}
}
func BenchmarkConvT2I(b *testing.B) {
func BenchmarkConvT2ILarge(b *testing.B) {
for i := 0; i < b.N; i++ {
W = B
i1 = tl
}
}
func BenchmarkConvI2E(b *testing.B) {
i2 = tm
for i := 0; i < b.N; i++ {
I = W
e = i2
}
}
func BenchmarkConvI2I(b *testing.B) {
i2 = tm
for i := 0; i < b.N; i++ {
W = R
i1 = i2
}
}
func BenchmarkAssertE2T(b *testing.B) {
I = 1
e = tm
for i := 0; i < b.N; i++ {
J = I.(int)
tm = e.(TM)
}
}
func BenchmarkAssertE2TBig(b *testing.B) {
var v interface{} = [2]*int{}
func BenchmarkAssertE2TLarge(b *testing.B) {
e = tl
for i := 0; i < b.N; i++ {
Big = v.([2]*int)
tl = e.(TL)
}
}
func BenchmarkAssertE2I(b *testing.B) {
e = tm
for i := 0; i < b.N; i++ {
W = I2.(io.Writer)
i1 = e.(I1)
}
}
func BenchmarkAssertI2T(b *testing.B) {
i1 = tm
for i := 0; i < b.N; i++ {
B = W.(*bytes.Buffer)
tm = i1.(TM)
}
}
func BenchmarkAssertI2I(b *testing.B) {
i1 = tm
for i := 0; i < b.N; i++ {
W = R.(io.Writer)
i2 = i1.(I2)
}
}
func BenchmarkAssertI2E(b *testing.B) {
i1 = tm
for i := 0; i < b.N; i++ {
I = R.(interface{})
e = i1.(interface{})
}
}
func BenchmarkAssertE2E(b *testing.B) {
e = tm
for i := 0; i < b.N; i++ {
I = I2.(interface{})
e_ = e
}
}
......@@ -8,27 +8,56 @@
package main
type J interface {
Method()
}
type (
U16 uint16
U32 uint32
U64 uint64
U128 [2]uint64
F32 float32
F64 float64
C128 complex128
S string
B []byte
M map[int]int
C chan int
Z struct{}
)
func (U16) Method() {}
func (U32) Method() {}
func (U64) Method() {}
func (U128) Method() {}
func (F32) Method() {}
func (F64) Method() {}
func (C128) Method() {}
func (S) Method() {}
func (B) Method() {}
func (M) Method() {}
func (C) Method() {}
func (Z) Method() {}
var (
z = struct{}{}
u16 = U16(1)
u32 = U32(2)
u64 = U64(3)
u128 = U128{4, 5}
f32 = F32(6)
f64 = F64(7)
c128 = C128(8 + 9i)
s = S("10")
b = B("11")
m = M{12: 13}
c = make(C, 14)
z = Z{}
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
......@@ -40,6 +69,24 @@ var (
ib interface{} = b
im interface{} = m
ic interface{} = c
iz interface{} = z
ip interface{} = p
ipp interface{} = pp
ju16 J = u16
ju32 J = u32
ju64 J = u64
ju128 J = u128
jf32 J = f32
jf64 J = f64
jc128 J = c128
js J = s
jb J = b
jm J = m
jc J = c
jz J = z
jp J = p // The method set for *T contains the methods for T.
// pp does not implement error.
)
func second(a ...interface{}) interface{} {
......@@ -47,44 +94,78 @@ func second(a ...interface{}) interface{} {
}
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")
}
// Test equality.
if u16 != iu16 {
panic("u16 != iu16")
}
if u16 != ju16 {
panic("u16 != ju16")
}
if u32 != iu32 {
panic("u32 != iu32")
}
if u32 != ju32 {
panic("u32 != ju32")
}
if u64 != iu64 {
panic("u64 != iu64")
}
if u64 != ju64 {
panic("u64 != ju64")
}
if u128 != iu128 {
panic("u128 != iu128")
}
if u128 != ju128 {
panic("u128 != ju128")
}
if f32 != if32 {
panic("f32 != if32")
}
if f32 != jf32 {
panic("f32 != jf32")
}
if f64 != if64 {
panic("f64 != if64")
}
if f64 != jf64 {
panic("f64 != jf64")
}
if c128 != ic128 {
panic("c128 != ic128")
}
if c128 != jc128 {
panic("c128 != jc128")
}
if s != is {
panic("s != is")
}
if s != js {
panic("s != js")
}
if c != ic {
panic("c != ic")
}
if c != jc {
panic("c != jc")
}
// There are no tests for b and m, as slices and maps are not comparable by ==.
if z != iz {
panic("z != iz")
}
if z != jz {
panic("z != jz")
}
if p != ip {
panic("p != ip")
}
if p != jp {
panic("p != jp")
}
if pp != ipp {
panic("pp != ipp")
}
// pp does not implement J.
// 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 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment