]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: use slice and bitmap for some attributes
authorCherry Mui <cherryyz@google.com>
Fri, 21 Apr 2023 03:33:42 +0000 (23:33 -0400)
committerCherry Mui <cherryyz@google.com>
Mon, 24 Apr 2023 14:00:39 +0000 (14:00 +0000)
Currently, a symbol's outer symbol, the "special" attribute, and
whether a symbol is a generator symbol are represented as maps,
and are accessed in some loops over nearly all reachable symbols.
The map lookups are a bit expensive.

For outer symbol, a non-trivial portion of the symbols have outer
symbol set (e.g. type symbols, which we put into container symbols
like "type:*"). Using a slice to access more efficiently.

For the special and generator symbol attributes, use a bitmap.
There are not many symbols have those attributes, so the bitmap is
quite sparse. The bitmap is not too large anyway, so use it for
now. If we want to further reduce memory usage we could consider
some other data structure like a Bloom filter.

Linking cmd/compile in external linking mode (on macOS/amd64)

Symtab   12.9ms ± 9%     6.4ms ± 5%   -50.08%  (p=0.000 n=19+18)
Dodata   64.9ms ±12%    57.1ms ±12%   -11.90%  (p=0.000 n=20+20)
Asmb     36.7ms ±11%    32.8ms ± 9%   -10.61%  (p=0.000 n=20+18)
Asmb2    26.6ms ±15%    21.9ms ±12%   -17.75%  (p=0.000 n=20+18)

There is some increase of memory usage

Munmap_GC   40.9M ± 1%     43.2M ± 0%    +5.54%  (p=0.000 n=20+19)

The next CL will bring the memory usage back.

Change-Id: Ie4347eb96c51f008b9284270de37fc880bb52d2c
Reviewed-on: https://go-review.googlesource.com/c/go/+/487415
Run-TryBot: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/loader/loader.go

index 49766d598d468195870d5bfa2c759e62fa533003..a4c52e942b04d83032491e6e29ea45bce18a0ba6 100644 (file)
@@ -1080,7 +1080,8 @@ func writeBlock(ctxt *Link, out *OutBuf, ldr *loader.Loader, syms []loader.Sym,
                }
                P := out.WriteSym(ldr, s)
                st.relocsym(s, P)
-               if f, ok := ctxt.generatorSyms[s]; ok {
+               if ldr.IsGeneratedSym(s) {
+                       f := ctxt.generatorSyms[s]
                        f(ctxt, s)
                }
                addr += int64(len(P))
index c94f597a0f7f268e06dcbdac944a9c747cbd2871..1dea66393db2d94fa196c5f63263f171edfc051e 100644 (file)
@@ -220,23 +220,19 @@ type Loader struct {
        attrLocal            Bitmap // "local" symbols, indexed by global index
        attrNotInSymbolTable Bitmap // "not in symtab" symbols, indexed by global idx
        attrUsedInIface      Bitmap // "used in interface" symbols, indexed by global idx
+       attrSpecial          Bitmap // "special" frame symbols, indexed by global idx
        attrVisibilityHidden Bitmap // hidden symbols, indexed by ext sym index
        attrDuplicateOK      Bitmap // dupOK symbols, indexed by ext sym index
        attrShared           Bitmap // shared symbols, indexed by ext sym index
        attrExternal         Bitmap // external symbols, indexed by ext sym index
+       generatedSyms        Bitmap // symbols that generate their content, indexed by ext sym idx
 
        attrReadOnly         map[Sym]bool     // readonly data for this sym
-       attrSpecial          map[Sym]struct{} // "special" frame symbols
        attrCgoExportDynamic map[Sym]struct{} // "cgo_export_dynamic" symbols
        attrCgoExportStatic  map[Sym]struct{} // "cgo_export_static" symbols
-       generatedSyms        map[Sym]struct{} // symbols that generate their content
 
        // Outer and Sub relations for symbols.
-       // TODO: figure out whether it's more efficient to just have these
-       // as fields on extSymPayload (note that this won't be a viable
-       // strategy if somewhere in the linker we set sub/outer for a
-       // non-external sym).
-       outer map[Sym]Sym
+       outer []Sym // indexed by global index
        sub   map[Sym]Sym
 
        dynimplib   map[Sym]string      // stores Dynimplib symbol attribute
@@ -318,7 +314,6 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorRepor
                extReader:            extReader,
                symsByName:           [2]map[string]Sym{make(map[string]Sym, 80000), make(map[string]Sym, 50000)}, // preallocate ~2MB for ABI0 and ~1MB for ABI1 symbols
                objByPkg:             make(map[string]uint32),
-               outer:                make(map[Sym]Sym),
                sub:                  make(map[Sym]Sym),
                dynimplib:            make(map[Sym]string),
                dynimpvers:           make(map[Sym]string),
@@ -332,10 +327,8 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorRepor
                plt:                  make(map[Sym]int32),
                got:                  make(map[Sym]int32),
                dynid:                make(map[Sym]int32),
-               attrSpecial:          make(map[Sym]struct{}),
                attrCgoExportDynamic: make(map[Sym]struct{}),
                attrCgoExportStatic:  make(map[Sym]struct{}),
-               generatedSyms:        make(map[Sym]struct{}),
                deferReturnTramp:     make(map[Sym]bool),
                extStaticSyms:        make(map[nameVer]Sym),
                builtinSyms:          make([]Sym, nbuiltin),
@@ -496,6 +489,7 @@ func (l *Loader) newExtSym(name string, ver int) Sym {
                l.extStart = i
        }
        l.growValues(int(i) + 1)
+       l.growOuter(int(i) + 1)
        l.growAttrBitmaps(int(i) + 1)
        pi := l.newPayload(name, ver)
        l.objSyms = append(l.objSyms, objSym{l.extReader.objidx, uint32(pi)})
@@ -1010,17 +1004,16 @@ func (l *Loader) SetAttrExternal(i Sym, v bool) {
 // address (i.e. Value) computed by the usual mechanism of
 // data.go:dodata() & data.go:address().
 func (l *Loader) AttrSpecial(i Sym) bool {
-       _, ok := l.attrSpecial[i]
-       return ok
+       return l.attrSpecial.Has(i)
 }
 
 // SetAttrSpecial sets the "special" property for a symbol (see
 // AttrSpecial).
 func (l *Loader) SetAttrSpecial(i Sym, v bool) {
        if v {
-               l.attrSpecial[i] = struct{}{}
+               l.attrSpecial.Set(i)
        } else {
-               delete(l.attrSpecial, i)
+               l.attrSpecial.Unset(i)
        }
 }
 
@@ -1072,8 +1065,10 @@ func (l *Loader) SetAttrCgoExportStatic(i Sym, v bool) {
 // generator symbol through the SetIsGeneratedSym. The functions for generator
 // symbols are kept in the Link context.
 func (l *Loader) IsGeneratedSym(i Sym) bool {
-       _, ok := l.generatedSyms[i]
-       return ok
+       if !l.IsExternal(i) {
+               return false
+       }
+       return l.generatedSyms.Has(l.extIndex(i))
 }
 
 // SetIsGeneratedSym marks symbols as generated symbols. Data shouldn't be
@@ -1084,9 +1079,9 @@ func (l *Loader) SetIsGeneratedSym(i Sym, v bool) {
                panic("only external symbols can be generated")
        }
        if v {
-               l.generatedSyms[i] = struct{}{}
+               l.generatedSyms.Set(l.extIndex(i))
        } else {
-               delete(l.generatedSyms, i)
+               l.generatedSyms.Unset(l.extIndex(i))
        }
 }
 
@@ -1730,19 +1725,24 @@ func (l *Loader) AddInteriorSym(container Sym, interior Sym) {
        l.outer[interior] = container
 }
 
-// OuterSym gets the outer symbol for host object loaded symbols.
+// OuterSym gets the outer/container symbol.
 func (l *Loader) OuterSym(i Sym) Sym {
-       // FIXME: add check for isExternal?
        return l.outer[i]
 }
 
 // SubSym gets the subsymbol for host object loaded symbols.
 func (l *Loader) SubSym(i Sym) Sym {
-       // NB: note -- no check for l.isExternal(), since I am pretty sure
-       // that later phases in the linker set subsym for "type:" syms
        return l.sub[i]
 }
 
+// growOuter grows the slice used to store outer symbol.
+func (l *Loader) growOuter(reqLen int) {
+       curLen := len(l.outer)
+       if reqLen > curLen {
+               l.outer = append(l.outer, make([]Sym, reqLen-curLen)...)
+       }
+}
+
 // SetCarrierSym declares that 'c' is the carrier or container symbol
 // for 's'. Carrier symbols are used in the linker to as a container
 // for a collection of sub-symbols where the content of the
@@ -1835,6 +1835,7 @@ func (l *Loader) growAttrBitmaps(reqLen int) {
                l.attrLocal = growBitmap(reqLen, l.attrLocal)
                l.attrNotInSymbolTable = growBitmap(reqLen, l.attrNotInSymbolTable)
                l.attrUsedInIface = growBitmap(reqLen, l.attrUsedInIface)
+               l.attrSpecial = growBitmap(reqLen, l.attrSpecial)
        }
        l.growExtAttrBitmaps()
 }
@@ -1847,6 +1848,7 @@ func (l *Loader) growExtAttrBitmaps() {
                l.attrDuplicateOK = growBitmap(extReqLen, l.attrDuplicateOK)
                l.attrShared = growBitmap(extReqLen, l.attrShared)
                l.attrExternal = growBitmap(extReqLen, l.attrExternal)
+               l.generatedSyms = growBitmap(extReqLen, l.generatedSyms)
        }
 }
 
@@ -2219,6 +2221,7 @@ func (l *Loader) LoadSyms(arch *sys.Arch) {
                loadObjRefs(l, o.r, arch)
        }
        l.values = make([]int64, l.NSym(), l.NSym()+1000) // +1000 make some room for external symbols
+       l.outer = make([]Sym, l.NSym(), l.NSym()+1000)
 }
 
 func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) {