From f7ac5da4956fb2db129848be331345ece8e7faa6 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 29 Aug 2016 17:56:15 -0700 Subject: [PATCH] cmd/compile: make internal objects directly print to printer MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Internal objects that satisfy the Printable interface can print directly to a printer w/o going through the conversion to a string first. Made printer.f understand and special-case %v so that Printable objects use the printer directly. This is work in progress and we may end up doing something else eventually (perhaps using fmt.Formatter) - or even undo these changes if this exploration doesn't get us to a significantly better place. Allocations numbers relative to commit c85b77c (still up, but reduced from most recent change): name old time/op new time/op delta Template 307ms ± 4% 315ms ± 4% +2.55% (p=0.000 n=29+29) Unicode 164ms ± 4% 165ms ± 4% ~ (p=0.057 n=30+30) GoTypes 1.01s ± 3% 1.03s ± 3% +1.72% (p=0.000 n=30+30) Compiler 5.49s ± 1% 5.62s ± 2% +2.31% (p=0.000 n=30+28) name old user-ns/op new user-ns/op delta Template 397M ± 3% 406M ± 6% +2.21% (p=0.000 n=28+30) Unicode 225M ± 4% 226M ± 3% ~ (p=0.230 n=29+30) GoTypes 1.31G ± 3% 1.34G ± 5% +2.79% (p=0.000 n=30+30) Compiler 7.39G ± 2% 7.50G ± 2% +1.43% (p=0.000 n=30+29) name old alloc/op new alloc/op delta Template 46.8MB ± 0% 47.5MB ± 0% +1.48% (p=0.000 n=29+28) Unicode 37.8MB ± 0% 38.1MB ± 0% +0.64% (p=0.000 n=30+28) GoTypes 143MB ± 0% 145MB ± 0% +1.72% (p=0.000 n=30+30) Compiler 683MB ± 0% 706MB ± 0% +3.31% (p=0.000 n=30+29) name old allocs/op new allocs/op delta Template 444k ± 0% 481k ± 0% +8.38% (p=0.000 n=30+30) Unicode 369k ± 0% 379k ± 0% +2.74% (p=0.000 n=30+30) GoTypes 1.35M ± 0% 1.50M ± 0% +10.78% (p=0.000 n=30+30) Compiler 5.66M ± 0% 6.25M ± 0% +10.31% (p=0.000 n=29+29) For #16897. Change-Id: I37f95ab60508018ee6d29a98d238482b60e3e4b5 Reviewed-on: https://go-review.googlesource.com/28072 Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/fmt.go | 126 +++++++++++++++++++++-------- 1 file changed, 92 insertions(+), 34 deletions(-) diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 6168ce88d3..1a898f49f6 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -537,7 +537,7 @@ func (p *printer) typefmt(t *Type, flag FmtFlag) *printer { if t == bytetype || t == runetype { // in %-T mode collapse rune and byte with their originals. if fmtmode != FTypeId { - return p.s(sconv(t.Sym, FmtShort)) + return p.sconv(t.Sym, FmtShort) } t = Types[t.Etype] } @@ -554,11 +554,11 @@ func (p *printer) typefmt(t *Type, flag FmtFlag) *printer { if t.Vargen != 0 { return p.f("%v·%d", sconv(t.Sym, FmtShort), t.Vargen) } - return p.s(sconv(t.Sym, FmtShort)) + return p.sconv(t.Sym, FmtShort) } if flag&FmtUnsigned != 0 { - return p.s(sconv(t.Sym, FmtUnsigned)) + return p.sconv(t.Sym, FmtUnsigned) } if t.Sym.Pkg == localpkg && t.Vargen != 0 { @@ -566,7 +566,7 @@ func (p *printer) typefmt(t *Type, flag FmtFlag) *printer { } } - return p.s(sconv(t.Sym, 0)) + return p.sconv(t.Sym, 0) } if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" { @@ -629,11 +629,11 @@ func (p *printer) typefmt(t *Type, flag FmtFlag) *printer { // Wrong interface definitions may have types lacking a symbol. break case exportname(f.Sym.Name): - p.s(sconv(f.Sym, FmtShort)) + p.sconv(f.Sym, FmtShort) default: - p.s(sconv(f.Sym, FmtUnsigned)) + p.sconv(f.Sym, FmtUnsigned) } - p.s(Tconv(f.Type, FmtShort)) + p.Tconv(f.Type, FmtShort) } if t.NumFields() != 0 { p.s(" ") @@ -646,12 +646,12 @@ func (p *printer) typefmt(t *Type, flag FmtFlag) *printer { } else { if t.Recv() != nil { p.s("method") - p.s(Tconv(t.Recvs(), 0)) + p.Tconv(t.Recvs(), 0) p.s(" ") } p.s("func") } - p.s(Tconv(t.Params(), 0)) + p.Tconv(t.Params(), 0) switch t.Results().NumFields() { case 0: @@ -659,11 +659,11 @@ func (p *printer) typefmt(t *Type, flag FmtFlag) *printer { case 1: p.s(" ") - p.s(Tconv(t.Results().Field(0).Type, 0)) // struct->field->field's type + p.Tconv(t.Results().Field(0).Type, 0) // struct->field->field's type default: p.s(" ") - p.s(Tconv(t.Results(), 0)) + p.Tconv(t.Results(), 0) } return p @@ -777,7 +777,7 @@ func (p *printer) stmtfmt(n *Node) *printer { if n.Left != nil { p.f("%v %v", n.Left, n.Right) } else { - p.s(Nconv(n.Right, 0)) + p.Nconv(n.Right, 0) } // Don't export "v = " initializing statements, hope they're always @@ -1075,7 +1075,7 @@ func (p *printer) exprfmt(n *Node, prec int) *printer { return p.exprfmt(n.Orig, prec) } if n.Sym != nil { - return p.s(sconv(n.Sym, 0)) + return p.sconv(n.Sym, 0) } } if n.Val().Ctype() == CTNIL && n.Orig != nil && n.Orig != n { @@ -1102,13 +1102,13 @@ func (p *printer) exprfmt(n *Node, prec int) *printer { fallthrough case OPACK, ONONAME: - return p.s(sconv(n.Sym, 0)) + return p.sconv(n.Sym, 0) case OTYPE: if n.Type == nil && n.Sym != nil { - return p.s(sconv(n.Sym, 0)) + return p.sconv(n.Sym, 0) } - return p.s(Tconv(n.Type, 0)) + return p.Tconv(n.Type, 0) case OTARRAY: if n.Left != nil { @@ -1450,10 +1450,10 @@ func (p *printer) nodedump(n *Node, flag FmtFlag) *printer { if recur { if n.Left != nil { - p.s(Nconv(n.Left, 0)) + p.Nconv(n.Left, 0) } if n.Right != nil { - p.s(Nconv(n.Right, 0)) + p.Nconv(n.Right, 0) } if n.List.Len() != 0 { p.indent() @@ -1474,6 +1474,12 @@ func (p *printer) nodedump(n *Node, flag FmtFlag) *printer { return p } +func (s *Sym) Print(p *printer) { + p.sconv(s, 0) +} + +var _ Printable = new(Sym) // verify that Sym implements Printable + func (s *Sym) String() string { return sconv(s, 0) } @@ -1481,18 +1487,20 @@ func (s *Sym) String() string { // Fmt "%S": syms // Flags: "%hS" suppresses qualifying with package func sconv(s *Sym, flag FmtFlag) string { - var p printer + return new(printer).sconv(s, flag).String() +} +func (p *printer) sconv(s *Sym, flag FmtFlag) *printer { if flag&FmtLong != 0 { panic("linksymfmt") } if s == nil { - return "" + return p.s("") } if s.Name == "_" { - return "_" + return p.s("_") } sf := flag @@ -1502,9 +1510,15 @@ func sconv(s *Sym, flag FmtFlag) string { fmtmode = sm fmtbody = sb - return p.String() + return p } +func (t *Type) Print(p *printer) { + p.Tconv(t, 0) +} + +var _ Printable = new(Type) // verify Type implements Printable + func (t *Type) String() string { return Tconv(t, 0) } @@ -1595,14 +1609,16 @@ func Fldconv(f *Field, flag FmtFlag) string { // 'h' omit 'func' and receiver from function types, short type names // 'u' package name, not prefix (FTypeId mode, sticky) func Tconv(t *Type, flag FmtFlag) string { - var p printer + return new(printer).Tconv(t, flag).String() +} +func (p *printer) Tconv(t *Type, flag FmtFlag) *printer { if t == nil { - return "" + return p.s("") } if t.Trecur > 4 { - return "<...>" + return p.s("<...>") } t.Trecur++ @@ -1627,9 +1643,15 @@ func Tconv(t *Type, flag FmtFlag) string { fmtmode = sm t.Trecur-- - return p.String() + return p +} + +func (n *Node) Print(p *printer) { + p.Nconv(n, 0) } +var _ Printable = new(Node) // verify that Node implements Printable + func (n *Node) String() string { return Nconv(n, 0) } @@ -1638,10 +1660,12 @@ func (n *Node) String() string { // Flags: 'l' suffix with "(type %T)" where possible // '+h' in debug mode, don't recurse, no multiline output func Nconv(n *Node, flag FmtFlag) string { - var p printer + return new(printer).Nconv(n, flag).String() +} +func (p *printer) Nconv(n *Node, flag FmtFlag) *printer { if n == nil { - return "" + return p.s("") } sf := flag sm, sb := setfmode(&flag) @@ -1663,9 +1687,15 @@ func Nconv(n *Node, flag FmtFlag) string { fmtbody = sb fmtmode = sm - return p.String() + return p } +func (n Nodes) Print(p *printer) { + p.hconv(n, 0) +} + +var _ Printable = Nodes{} // verify that Nodes implements Printable + func (n Nodes) String() string { return hconv(n, 0) } @@ -1673,10 +1703,12 @@ func (n Nodes) String() string { // Fmt '%H': Nodes. // Flags: all those of %N plus ',': separate with comma's instead of semicolons. func hconv(l Nodes, flag FmtFlag) string { - var p printer + return new(printer).hconv(l, flag).String() +} +func (p *printer) hconv(l Nodes, flag FmtFlag) *printer { if l.Len() == 0 && fmtmode == FDbg { - return "" + return p.s("") } sf := flag @@ -1689,7 +1721,7 @@ func hconv(l Nodes, flag FmtFlag) string { } for i, n := range l.Slice() { - p.s(Nconv(n, 0)) + p.Nconv(n, 0) if i+1 < l.Len() { p.s(sep) } @@ -1699,7 +1731,7 @@ func hconv(l Nodes, flag FmtFlag) string { fmtbody = sb fmtmode = sm - return p.String() + return p } func dumplist(s string, l Nodes) { @@ -1715,6 +1747,13 @@ type printer struct { buf []byte } +// Types that implement the Printable interface print +// to a printer directly without first converting to +// a string. +type Printable interface { + Print(*printer) +} + // printer implements io.Writer. func (p *printer) Write(buf []byte) (n int, err error) { p.buf = append(p.buf, buf...) @@ -1733,8 +1772,27 @@ func (p *printer) s(s string) *printer { } // f prints the formatted arguments to p and returns p. +// %v arguments that implement the Printable interface +// are printed to p via that interface. func (p *printer) f(format string, args ...interface{}) *printer { - fmt.Fprintf(p, format, args...) + for len(format) > 0 { + i := strings.IndexByte(format, '%') + if i < 0 || i+1 >= len(format) || format[i+1] != 'v' || len(args) == 0 { + break // don't be clever, let fmt.Fprintf handle this for now + } + // found "%v" and at least one argument (and no other %x before) + p.s(format[:i]) + format = format[i+len("%v"):] + if a, ok := args[0].(Printable); ok { + a.Print(p) + } else { + fmt.Fprintf(p, "%v", args[0]) + } + args = args[1:] + } + if len(format) > 0 || len(args) > 0 { + fmt.Fprintf(p, format, args...) + } return p } -- 2.48.1