From 90a59d448e152d790c2043a5777d9ee496365bcf Mon Sep 17 00:00:00 2001 From: Shahar Kohanim Date: Wed, 9 Mar 2016 16:23:25 +0200 Subject: [PATCH] cmd/link: use stdlib sort in dodata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Speeds up linking cmd/go by 1.7% name old s/op new s/op delta LinkCmdGo 0.58 ± 4% 0.57 ± 5% -1.74% (p=0.000 n=96+97) Change-Id: I7844cf4e2eeac260318de2b6ddf52ce07a6e00f5 Reviewed-on: https://go-review.googlesource.com/20915 TryBot-Result: Gobot Gobot Reviewed-by: David Crawshaw --- src/cmd/link/internal/ld/data.go | 84 ++++++++++++++++++++++++-------- src/cmd/link/internal/ld/util.go | 11 ----- 2 files changed, 65 insertions(+), 30 deletions(-) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index a4474baf9f..a5901c52c6 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -37,6 +37,7 @@ import ( "fmt" "log" "os" + "sort" "strconv" "strings" ) @@ -216,24 +217,6 @@ func addaddrplus4(ctxt *Link, s *LSym, t *LSym, add int64) int64 { * sort of LSym* structures. * Used for the data block. */ -func datcmp(s1 *LSym, s2 *LSym) int { - if s1.Type != s2.Type { - return int(s1.Type) - int(s2.Type) - } - - // For ppc64, we want to interleave the .got and .toc sections - // from input files. Both are type SELFGOT, so in that case - // fall through to the name comparison (conveniently, .got - // sorts before .toc). - if s1.Type != obj.SELFGOT && s1.Size != s2.Size { - if s1.Size < s2.Size { - return -1 - } - return +1 - } - - return stringsCompare(s1.Name, s2.Name) -} func listnextp(s *LSym) **LSym { return &s.Next @@ -1127,6 +1110,36 @@ func (p *GCProg) AddSym(s *LSym) { p.w.Append(prog[4:], nptr) } +type dataSortKey struct { + // keep sort keys inline to improve cache behaviour while sorting + Type int16 + Size int64 + Name string + + Lsym *LSym +} + +type dataSlice []dataSortKey + +func (d dataSlice) Len() int { return len(d) } +func (d dataSlice) Swap(i, j int) { d[i], d[j] = d[j], d[i] } +func (d dataSlice) Less(i, j int) bool { + s1, s2 := &d[i], &d[j] + if s1.Type != s2.Type { + return s1.Type < s2.Type + } + + // For ppc64, we want to interleave the .got and .toc sections + // from input files. Both are type SELFGOT, so in that case + // fall through to the name comparison (conveniently, .got + // sorts before .toc). + if s1.Type != obj.SELFGOT && s1.Size != s2.Size { + return s1.Size < s2.Size + } + + return s1.Name < s2.Name +} + func growdatsize(datsizep *int64, s *LSym) { datsize := *datsizep const cutoff int64 = 2e9 // 2 GB (or so; looks better in errors than 2^31) @@ -1141,6 +1154,39 @@ func growdatsize(datsizep *int64, s *LSym) { *datsizep = datsize + s.Size } +func list2Slice(head *LSym) dataSlice { + n := 0 + for s := datap; s != nil; s = s.Next { + n++ + } + slice := make(dataSlice, n) + i := 0 + for s := datap; s != nil; s = s.Next { + k := &slice[i] + k.Type = s.Type + k.Size = s.Size + k.Name = s.Name + k.Lsym = s + + i++ + } + return slice +} + +func slice2List(d dataSlice) *LSym { + for i := 0; i < len(d)-1; i++ { + d[i].Lsym.Next = d[i+1].Lsym + } + d[len(d)-1].Lsym.Next = nil + return d[0].Lsym +} + +func dataSort(head *LSym) *LSym { + d := list2Slice(head) + sort.Sort(d) + return slice2List(d) +} + func dodata() { if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f dodata\n", obj.Cputime()) @@ -1231,7 +1277,7 @@ func dodata() { } - datap = listsort(datap, datcmp, listnextp) + datap = dataSort(datap) if Iself { // Make .rela and .rela.plt contiguous, the ELF ABI requires this diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go index e84d17d308..19b3688aff 100644 --- a/src/cmd/link/internal/ld/util.go +++ b/src/cmd/link/internal/ld/util.go @@ -67,17 +67,6 @@ func tokenize(s string) []string { return f } -// strings.Compare, introduced in Go 1.5. -func stringsCompare(a, b string) int { - if a == b { - return 0 - } - if a < b { - return -1 - } - return +1 -} - var atExitFuncs []func() func AtExit(f func()) {