mirror of
https://github.com/golang/go
synced 2024-11-21 12:24:39 -07:00
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
This commit is contained in:
parent
917f764382
commit
18e86644a3
@ -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 BenchmarkConvT2EBig(b *testing.B) {
|
||||
v := [2]uintptr{1, 2}
|
||||
func BenchmarkConvT2ELarge(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
I = v
|
||||
e = tl
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkConvT2I(b *testing.B) {
|
||||
func BenchmarkConvT2ISmall(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
W = B
|
||||
i1 = ts
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkConvT2IUintptr(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
i1 = tm
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkConvT2ILarge(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
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 {
|
Loading…
Reference in New Issue
Block a user