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) ...@@ -208,7 +208,7 @@ ggloblnod(Node *nam, int32 width)
} }
void void
ggloblsym(Sym *s, int32 width, int dupok) ggloblsym(Sym *s, int32 width, int dupok, int rodata)
{ {
Prog *p; Prog *p;
...@@ -220,7 +220,8 @@ ggloblsym(Sym *s, int32 width, int dupok) ...@@ -220,7 +220,8 @@ ggloblsym(Sym *s, int32 width, int dupok)
p->to.name = D_NONE; p->to.name = D_NONE;
p->to.offset = width; p->to.offset = width;
if(dupok) if(dupok)
p->reg = DUPOK; p->reg |= DUPOK;
if(rodata)
p->reg |= RODATA; p->reg |= RODATA;
} }
......
...@@ -206,7 +206,7 @@ ggloblnod(Node *nam, int32 width) ...@@ -206,7 +206,7 @@ ggloblnod(Node *nam, int32 width)
} }
void void
ggloblsym(Sym *s, int32 width, int dupok) ggloblsym(Sym *s, int32 width, int dupok, int rodata)
{ {
Prog *p; Prog *p;
...@@ -218,7 +218,8 @@ ggloblsym(Sym *s, int32 width, int dupok) ...@@ -218,7 +218,8 @@ ggloblsym(Sym *s, int32 width, int dupok)
p->to.index = D_NONE; p->to.index = D_NONE;
p->to.offset = width; p->to.offset = width;
if(dupok) if(dupok)
p->from.scale = DUPOK; p->from.scale |= DUPOK;
if(rodata)
p->from.scale |= RODATA; p->from.scale |= RODATA;
} }
......
...@@ -207,7 +207,7 @@ ggloblnod(Node *nam, int32 width) ...@@ -207,7 +207,7 @@ ggloblnod(Node *nam, int32 width)
} }
void void
ggloblsym(Sym *s, int32 width, int dupok) ggloblsym(Sym *s, int32 width, int dupok, int rodata)
{ {
Prog *p; Prog *p;
...@@ -219,7 +219,8 @@ ggloblsym(Sym *s, int32 width, int dupok) ...@@ -219,7 +219,8 @@ ggloblsym(Sym *s, int32 width, int dupok)
p->to.index = D_NONE; p->to.index = D_NONE;
p->to.offset = width; p->to.offset = width;
if(dupok) if(dupok)
p->from.scale = DUPOK; p->from.scale |= DUPOK;
if(rodata)
p->from.scale |= RODATA; p->from.scale |= RODATA;
} }
......
...@@ -41,7 +41,7 @@ char *runtimeimport = ...@@ -41,7 +41,7 @@ char *runtimeimport =
"func @\"\".convI2E(@\"\".elem any) (@\"\".ret any)\n" "func @\"\".convI2E(@\"\".elem any) (@\"\".ret any)\n"
"func @\"\".convI2I(@\"\".typ *byte, @\"\".elem any) (@\"\".ret any)\n" "func @\"\".convI2I(@\"\".typ *byte, @\"\".elem any) (@\"\".ret any)\n"
"func @\"\".convT2E(@\"\".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 @\"\".assertE2E(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n"
"func @\"\".assertE2E2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" "func @\"\".assertE2E2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n"
"func @\"\".assertE2I(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n" "func @\"\".assertE2I(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n"
......
...@@ -767,6 +767,7 @@ EXTERN Pkg* importpkg; // package being imported ...@@ -767,6 +767,7 @@ EXTERN Pkg* importpkg; // package being imported
EXTERN Pkg* structpkg; // package that declared struct, during import EXTERN Pkg* structpkg; // package that declared struct, during import
EXTERN Pkg* builtinpkg; // fake package for builtins EXTERN Pkg* builtinpkg; // fake package for builtins
EXTERN Pkg* gostringpkg; // fake pkg for Go strings EXTERN Pkg* gostringpkg; // fake pkg for Go strings
EXTERN Pkg* itabpkg; // fake pkg for itab cache
EXTERN Pkg* runtimepkg; // package runtime EXTERN Pkg* runtimepkg; // package runtime
EXTERN Pkg* stringpkg; // fake package for C strings EXTERN Pkg* stringpkg; // fake package for C strings
EXTERN Pkg* typepkg; // fake package for runtime type info EXTERN Pkg* typepkg; // fake package for runtime type info
...@@ -1330,7 +1331,7 @@ void gdatacomplex(Node*, Mpcplx*); ...@@ -1330,7 +1331,7 @@ void gdatacomplex(Node*, Mpcplx*);
void gdatastring(Node*, Strlit*); void gdatastring(Node*, Strlit*);
void genembedtramp(Type*, Type*, Sym*, int iface); void genembedtramp(Type*, Type*, Sym*, int iface);
void ggloblnod(Node *nam, int32 width); 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*); Prog* gjmp(Prog*);
void gused(Node*); void gused(Node*);
int isfat(Type*); int isfat(Type*);
......
...@@ -204,6 +204,10 @@ main(int argc, char *argv[]) ...@@ -204,6 +204,10 @@ main(int argc, char *argv[])
gostringpkg->name = "go.string"; gostringpkg->name = "go.string";
gostringpkg->prefix = "go.string"; // not go%2estring 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 = mkpkg(strlit("runtime"));
runtimepkg->name = "runtime"; runtimepkg->name = "runtime";
......
...@@ -314,7 +314,7 @@ stringsym(char *s, int len) ...@@ -314,7 +314,7 @@ stringsym(char *s, int len)
} }
off = duint8(sym, off, 0); // terminating NUL for runtime off = duint8(sym, off, 0); // terminating NUL for runtime
off = (off+widthptr-1)&~(widthptr-1); // round to pointer alignment off = (off+widthptr-1)&~(widthptr-1); // round to pointer alignment
ggloblsym(sym, off, 1); ggloblsym(sym, off, 1, 1);
return sym; return sym;
} }
...@@ -305,7 +305,7 @@ dimportpath(Pkg *p) ...@@ -305,7 +305,7 @@ dimportpath(Pkg *p)
p->pathsym = n->sym; p->pathsym = n->sym;
gdatastring(n, p->path); gdatastring(n, p->path);
ggloblsym(n->sym, types[TSTRING]->width, 1); ggloblsym(n->sym, types[TSTRING]->width, 1, 1);
} }
static int static int
...@@ -857,7 +857,7 @@ ok: ...@@ -857,7 +857,7 @@ ok:
break; break;
} }
ot = dextratype(s, ot, t, xt); ot = dextratype(s, ot, t, xt);
ggloblsym(s, ot, dupok); ggloblsym(s, ot, dupok, 1);
return s; return s;
} }
...@@ -955,7 +955,7 @@ dalgsym(Type *t) ...@@ -955,7 +955,7 @@ dalgsym(Type *t)
break; break;
} }
ggloblsym(s, ot, 1); ggloblsym(s, ot, 1, 1);
return s; return s;
} }
...@@ -61,7 +61,7 @@ func slicestringcopy(to any, fr any) int ...@@ -61,7 +61,7 @@ func slicestringcopy(to any, fr any) int
func convI2E(elem any) (ret any) func convI2E(elem any) (ret any)
func convI2I(typ *byte, elem any) (ret any) func convI2I(typ *byte, elem any) (ret any)
func convT2E(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) // interface type assertions x.(T)
func assertE2E(typ *byte, iface any) (ret any) func assertE2E(typ *byte, iface any) (ret any)
......
...@@ -375,6 +375,7 @@ walkexpr(Node **np, NodeList **init) ...@@ -375,6 +375,7 @@ walkexpr(Node **np, NodeList **init)
int64 v; int64 v;
int32 lno; int32 lno;
Node *n, *fn; Node *n, *fn;
Sym *sym;
char buf[100], *p; char buf[100], *p;
n = *np; n = *np;
...@@ -755,6 +756,22 @@ walkexpr(Node **np, NodeList **init) ...@@ -755,6 +756,22 @@ walkexpr(Node **np, NodeList **init)
ll = list(ll, typename(n->left->type)); ll = list(ll, typename(n->left->type));
if(!isnilinter(n->type)) if(!isnilinter(n->type))
ll = list(ll, typename(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); ll = list(ll, n->left);
argtype(fn, n->left->type); argtype(fn, n->left->type);
argtype(fn, n->type); argtype(fn, n->type);
......
...@@ -183,19 +183,25 @@ copyout(Type *t, void **src, void *dst) ...@@ -183,19 +183,25 @@ copyout(Type *t, void **src, void *dst)
alg->copy(size, dst, *src); 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 #pragma textflag 7
void void
runtime·convT2I(Type *t, InterfaceType *inter, ...) runtime·convT2I(Type *t, InterfaceType *inter, Itab **cache, ...)
{ {
byte *elem; byte *elem;
Iface *ret; Iface *ret;
Itab *tab;
int32 wid; int32 wid;
elem = (byte*)(&inter+1); elem = (byte*)(&cache+1);
wid = t->size; wid = t->size;
ret = (Iface*)(elem + ROUND(wid, Structrnd)); 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); copyin(t, elem, &ret->data);
} }
......
...@@ -5,98 +5,134 @@ ...@@ -5,98 +5,134 @@
package runtime_test package runtime_test
import ( import (
"bytes"
"io"
"testing" "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 ( var (
I interface{} e interface{}
J int e_ interface{}
B = new(bytes.Buffer) i1 I1
W io.Writer = B i2 I2
I2 interface{} = B ts TS
R io.ReadWriter = B tm TM
Big [2]*int tl TL
) )
func BenchmarkConvT2ESmall(b *testing.B) { func BenchmarkConvT2ESmall(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
I = uint16(1) e = ts
} }
} }
func BenchmarkConvT2EUintptr(b *testing.B) { func BenchmarkConvT2EUintptr(b *testing.B) {
for i := 0; i < b.N; i++ { 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) { func BenchmarkConvT2IUintptr(b *testing.B) {
v := [2]uintptr{1, 2}
for i := 0; i < b.N; i++ { 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++ { for i := 0; i < b.N; i++ {
W = B i1 = tl
} }
} }
func BenchmarkConvI2E(b *testing.B) { func BenchmarkConvI2E(b *testing.B) {
i2 = tm
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
I = W e = i2
} }
} }
func BenchmarkConvI2I(b *testing.B) { func BenchmarkConvI2I(b *testing.B) {
i2 = tm
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
W = R i1 = i2
} }
} }
func BenchmarkAssertE2T(b *testing.B) { func BenchmarkAssertE2T(b *testing.B) {
I = 1 e = tm
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
J = I.(int) tm = e.(TM)
} }
} }
func BenchmarkAssertE2TBig(b *testing.B) { func BenchmarkAssertE2TLarge(b *testing.B) {
var v interface{} = [2]*int{} e = tl
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Big = v.([2]*int) tl = e.(TL)
} }
} }
func BenchmarkAssertE2I(b *testing.B) { func BenchmarkAssertE2I(b *testing.B) {
e = tm
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
W = I2.(io.Writer) i1 = e.(I1)
} }
} }
func BenchmarkAssertI2T(b *testing.B) { func BenchmarkAssertI2T(b *testing.B) {
i1 = tm
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
B = W.(*bytes.Buffer) tm = i1.(TM)
} }
} }
func BenchmarkAssertI2I(b *testing.B) { func BenchmarkAssertI2I(b *testing.B) {
i1 = tm
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
W = R.(io.Writer) i2 = i1.(I2)
} }
} }
func BenchmarkAssertI2E(b *testing.B) { func BenchmarkAssertI2E(b *testing.B) {
i1 = tm
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
I = R.(interface{}) e = i1.(interface{})
} }
} }
func BenchmarkAssertE2E(b *testing.B) { func BenchmarkAssertE2E(b *testing.B) {
e = tm
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
I = I2.(interface{}) e_ = e
} }
} }
...@@ -8,27 +8,56 @@ ...@@ -8,27 +8,56 @@
package main 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 ( 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 p = &z
pp = &p 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 ( var (
iz interface{} = z
ip interface{} = p
ipp interface{} = pp
iu16 interface{} = u16 iu16 interface{} = u16
iu32 interface{} = u32 iu32 interface{} = u32
iu64 interface{} = u64 iu64 interface{} = u64
...@@ -40,6 +69,24 @@ var ( ...@@ -40,6 +69,24 @@ var (
ib interface{} = b ib interface{} = b
im interface{} = m im interface{} = m
ic interface{} = c 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{} { func second(a ...interface{}) interface{} {
...@@ -47,44 +94,78 @@ func second(a ...interface{}) interface{} { ...@@ -47,44 +94,78 @@ func second(a ...interface{}) interface{} {
} }
func main() { func main() {
// Test equality. There are no tests for b and m, as slices and // Test equality.
// 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 { if u16 != iu16 {
panic("u16 != iu16") panic("u16 != iu16")
} }
if u16 != ju16 {
panic("u16 != ju16")
}
if u32 != iu32 { if u32 != iu32 {
panic("u32 != iu32") panic("u32 != iu32")
} }
if u32 != ju32 {
panic("u32 != ju32")
}
if u64 != iu64 { if u64 != iu64 {
panic("u64 != iu64") panic("u64 != iu64")
} }
if u64 != ju64 {
panic("u64 != ju64")
}
if u128 != iu128 { if u128 != iu128 {
panic("u128 != iu128") panic("u128 != iu128")
} }
if u128 != ju128 {
panic("u128 != ju128")
}
if f32 != if32 { if f32 != if32 {
panic("f32 != if32") panic("f32 != if32")
} }
if f32 != jf32 {
panic("f32 != jf32")
}
if f64 != if64 { if f64 != if64 {
panic("f64 != if64") panic("f64 != if64")
} }
if f64 != jf64 {
panic("f64 != jf64")
}
if c128 != ic128 { if c128 != ic128 {
panic("c128 != ic128") panic("c128 != ic128")
} }
if c128 != jc128 {
panic("c128 != jc128")
}
if s != is { if s != is {
panic("s != is") panic("s != is")
} }
if s != js {
panic("s != js")
}
if c != ic { if c != ic {
panic("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. // 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 { 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