Commit 36dbdbe9 authored by Cherry Zhang's avatar Cherry Zhang

[dev.link] cmd/link: use min-heap for work queue for better locality

In the deadcode pass, we use a work queue for the flood algorithm.
Currently this is a simple LIFO queue. In this order, there is
poor locality in accessing object files.

Since the global indices are assigned in package DAG order, edges
are mostly either within a package or from a smaller index to a
larger one. (With named symbols, there can be backward edges, but
shouldn't be too many.) Using a min-heap for the work queue, we
access all symbols in one object, then move to next one. It
rarely needs to revisit an object that is already visted. This
should result in better locality.

Benchmark result from Than (thanks!):

name                      old time/op       new time/op       delta
LinkCompiler                    1.74s ±11%        1.61s ± 9%  -7.80%  (p=0.000 n=20+19)
LinkWithoutDebugCompiler        1.27s ±11%        1.15s ± 9%  -9.02%  (p=0.000 n=20+20)

Currently this uses the container/heap package, which uses
interface elements. If this allocates too much, we may consider
to hand-code the min heap.

Change-Id: I216d5291c432fe1f40b0b8f4f1b9d388807bf6c5
Reviewed-on: https://go-review.googlesource.com/c/go/+/204438Reviewed-by: default avatarJeremy Faller <jeremy@golang.org>
parent 24ea07d5
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"cmd/internal/sys" "cmd/internal/sys"
"cmd/link/internal/loader" "cmd/link/internal/loader"
"cmd/link/internal/sym" "cmd/link/internal/sym"
"container/heap"
"fmt" "fmt"
"unicode" "unicode"
) )
...@@ -23,8 +24,17 @@ var _ = fmt.Print ...@@ -23,8 +24,17 @@ var _ = fmt.Print
type workQueue []loader.Sym type workQueue []loader.Sym
func (q *workQueue) push(i loader.Sym) { *q = append(*q, i) } // Implement container/heap.Interface.
func (q *workQueue) pop() loader.Sym { i := (*q)[len(*q)-1]; *q = (*q)[:len(*q)-1]; return i } func (q *workQueue) Len() int { return len(*q) }
func (q *workQueue) Less(i, j int) bool { return (*q)[i] < (*q)[j] }
func (q *workQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }
func (q *workQueue) Push(i interface{}) { *q = append(*q, i.(loader.Sym)) }
func (q *workQueue) Pop() interface{} { i := (*q)[len(*q)-1]; *q = (*q)[:len(*q)-1]; return i }
// Functions for deadcode pass to use.
// Deadcode pass should call push/pop, not Push/Pop.
func (q *workQueue) push(i loader.Sym) { heap.Push(q, i) }
func (q *workQueue) pop() loader.Sym { return heap.Pop(q).(loader.Sym) }
func (q *workQueue) empty() bool { return len(*q) == 0 } func (q *workQueue) empty() bool { return len(*q) == 0 }
type deadcodePass2 struct { type deadcodePass2 struct {
...@@ -44,6 +54,7 @@ func (d *deadcodePass2) init() { ...@@ -44,6 +54,7 @@ func (d *deadcodePass2) init() {
if d.ctxt.Reachparent != nil { if d.ctxt.Reachparent != nil {
d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym()) d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym())
} }
heap.Init(&d.wq)
if d.ctxt.BuildMode == BuildModeShared { if d.ctxt.BuildMode == BuildModeShared {
// Mark all symbols defined in this library as reachable when // Mark all symbols defined in this library as reachable when
......
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