Commit e6ad619a authored by Daniel Martí's avatar Daniel Martí

cmd/go: further reduce init work

The first biggest offender was crypto/des.init at ~1%. It's
cryptographically broken and the init function is relatively expensive,
which is unfortunate as both crypto/tls and crypto/x509 (and by
extension, cmd/go) import it. Hide the work behind sync.Once.

The second biggest offender was flag.sortFlags at just under 1%, used by
the Visit flagset methods. It allocated two slices, which made a
difference as cmd/go iterates over multiple flagsets during init.
Use a single slice with a direct sort.Interface implementation.

Another big offender is initializing global maps. Reducing this work in
cmd/go/internal/imports and net/textproto gives us close to another
whole 1% in saved work. The former can use map literals, and the latter
can hide the work behind sync.Once.

Finally, compress/flate used newHuffmanBitWriter as part of init, which
allocates many objects and slices. Yet it only used one of the slice
fields. Allocating just that slice saves a surprising ~0.3%, since we
generated a lot of unnecessary garbage.

All in all, these little pieces amount to just over 3% saved CPU time.

name         old time/op  new time/op  delta
ExecGoEnv-8  3.61ms ± 1%  3.50ms ± 0%  -3.02%  (p=0.000 n=10+10)

Updates #26775.
Updates #29382.

Change-Id: I915416e88a874c63235ba512617c8aef35c0ca8b
Reviewed-on: https://go-review.googlesource.com/c/go/+/166459
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 2d41444a
......@@ -195,17 +195,46 @@ func MatchFile(name string, tags map[string]bool) bool {
return true
}
var KnownOS = make(map[string]bool)
var KnownArch = make(map[string]bool)
func init() {
for _, v := range strings.Fields(goosList) {
KnownOS[v] = true
}
for _, v := range strings.Fields(goarchList) {
KnownArch[v] = true
}
var KnownOS = map[string]bool{
"aix": true,
"android": true,
"darwin": true,
"dragonfly": true,
"freebsd": true,
"hurd": true,
"js": true,
"linux": true,
"nacl": true,
"netbsd": true,
"openbsd": true,
"plan9": true,
"solaris": true,
"windows": true,
"zos": true,
}
const goosList = "aix android darwin dragonfly freebsd hurd js linux nacl netbsd openbsd plan9 solaris windows zos "
const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc riscv riscv64 s390 s390x sparc sparc64 wasm "
var KnownArch = map[string]bool{
"386": true,
"amd64": true,
"amd64p32": true,
"arm": true,
"armbe": true,
"arm64": true,
"arm64be": true,
"ppc64": true,
"ppc64le": true,
"mips": true,
"mipsle": true,
"mips64": true,
"mips64le": true,
"mips64p32": true,
"mips64p32le": true,
"ppc": true,
"riscv": true,
"riscv64": true,
"s390": true,
"s390x": true,
"sparc": true,
"sparc64": true,
"wasm": true,
}
......@@ -609,10 +609,10 @@ func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode)
var huffOffset *huffmanEncoder
func init() {
w := newHuffmanBitWriter(nil)
w.offsetFreq[0] = 1
offsetFreq := make([]int32, offsetCodeCount)
offsetFreq[0] = 1
huffOffset = newHuffmanEncoder(offsetCodeCount)
huffOffset.generate(w.offsetFreq, 15)
huffOffset.generate(offsetFreq, 15)
}
// writeBlockHuff encodes a block of bytes as either
......
......@@ -4,7 +4,10 @@
package des
import "encoding/binary"
import (
"encoding/binary"
"sync"
)
func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) {
b := binary.BigEndian.Uint64(src)
......@@ -42,7 +45,8 @@ func decryptBlock(subkeys []uint64, dst, src []byte) {
cryptBlock(subkeys, dst, src, true)
}
// DES Feistel function
// DES Feistel function. feistelBox must be initialized via
// feistelBoxOnce.Do(initFeistelBox) first.
func feistel(l, r uint32, k0, k1 uint64) (lout, rout uint32) {
var t uint32
......@@ -77,6 +81,8 @@ func feistel(l, r uint32, k0, k1 uint64) (lout, rout uint32) {
// for sBoxes[s][i][j] << 4*(7-s)
var feistelBox [8][64]uint32
var feistelBoxOnce sync.Once
// general purpose function to perform DES block permutations
func permuteBlock(src uint64, permutation []uint8) (block uint64) {
for position, n := range permutation {
......@@ -86,7 +92,7 @@ func permuteBlock(src uint64, permutation []uint8) (block uint64) {
return
}
func init() {
func initFeistelBox() {
for s := range sBoxes {
for i := 0; i < 4; i++ {
for j := 0; j < 16; j++ {
......@@ -219,6 +225,8 @@ func ksRotate(in uint32) (out []uint32) {
// creates 16 56-bit subkeys from the original key
func (c *desCipher) generateSubkeys(keyBytes []byte) {
feistelBoxOnce.Do(initFeistelBox)
// apply PC1 permutation to key
key := binary.BigEndian.Uint64(keyBytes)
permutedKey := permuteBlock(key, permutedChoice1[:])
......
......@@ -341,17 +341,15 @@ type Flag struct {
// sortFlags returns the flags as a slice in lexicographical sorted order.
func sortFlags(flags map[string]*Flag) []*Flag {
list := make(sort.StringSlice, len(flags))
result := make([]*Flag, len(flags))
i := 0
for _, f := range flags {
list[i] = f.Name
result[i] = f
i++
}
list.Sort()
result := make([]*Flag, len(list))
for i, name := range list {
result[i] = flags[name]
}
sort.Slice(result, func(i, j int) bool {
return result[i].Name < result[j].Name
})
return result
}
......
......@@ -11,6 +11,7 @@ import (
"io/ioutil"
"strconv"
"strings"
"sync"
)
// A Reader implements convenience methods for reading requests
......@@ -27,6 +28,7 @@ type Reader struct {
// should be reading from an io.LimitReader or similar Reader to bound
// the size of responses.
func NewReader(r *bufio.Reader) *Reader {
commonHeaderOnce.Do(initCommonHeader)
return &Reader{R: r}
}
......@@ -571,6 +573,8 @@ func (r *Reader) upcomingHeaderNewlines() (n int) {
// If s contains a space or invalid header field bytes, it is
// returned without modifications.
func CanonicalMIMEHeaderKey(s string) string {
commonHeaderOnce.Do(initCommonHeader)
// Quick check for canonical encoding.
upper := true
for i := 0; i < len(s); i++ {
......@@ -642,9 +646,12 @@ func canonicalMIMEHeaderKey(a []byte) string {
}
// commonHeader interns common header strings.
var commonHeader = make(map[string]string)
var commonHeader map[string]string
var commonHeaderOnce sync.Once
func init() {
func initCommonHeader() {
commonHeader = make(map[string]string)
for _, v := range []string{
"Accept",
"Accept-Charset",
......
......@@ -338,6 +338,7 @@ func TestReadMultiLineError(t *testing.T) {
}
func TestCommonHeaders(t *testing.T) {
commonHeaderOnce.Do(initCommonHeader)
for h := range commonHeader {
if h != CanonicalMIMEHeaderKey(h) {
t.Errorf("Non-canonical header %q in commonHeader", h)
......
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