Skip to content

Commit e6ad619

Browse files
committed
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í <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 2d41444 commit e6ad619

File tree

6 files changed

+70
-27
lines changed

6 files changed

+70
-27
lines changed

src/cmd/go/internal/imports/build.go

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -195,17 +195,46 @@ func MatchFile(name string, tags map[string]bool) bool {
195195
return true
196196
}
197197

198-
var KnownOS = make(map[string]bool)
199-
var KnownArch = make(map[string]bool)
200-
201-
func init() {
202-
for _, v := range strings.Fields(goosList) {
203-
KnownOS[v] = true
204-
}
205-
for _, v := range strings.Fields(goarchList) {
206-
KnownArch[v] = true
207-
}
198+
var KnownOS = map[string]bool{
199+
"aix": true,
200+
"android": true,
201+
"darwin": true,
202+
"dragonfly": true,
203+
"freebsd": true,
204+
"hurd": true,
205+
"js": true,
206+
"linux": true,
207+
"nacl": true,
208+
"netbsd": true,
209+
"openbsd": true,
210+
"plan9": true,
211+
"solaris": true,
212+
"windows": true,
213+
"zos": true,
208214
}
209215

210-
const goosList = "aix android darwin dragonfly freebsd hurd js linux nacl netbsd openbsd plan9 solaris windows zos "
211-
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 "
216+
var KnownArch = map[string]bool{
217+
"386": true,
218+
"amd64": true,
219+
"amd64p32": true,
220+
"arm": true,
221+
"armbe": true,
222+
"arm64": true,
223+
"arm64be": true,
224+
"ppc64": true,
225+
"ppc64le": true,
226+
"mips": true,
227+
"mipsle": true,
228+
"mips64": true,
229+
"mips64le": true,
230+
"mips64p32": true,
231+
"mips64p32le": true,
232+
"ppc": true,
233+
"riscv": true,
234+
"riscv64": true,
235+
"s390": true,
236+
"s390x": true,
237+
"sparc": true,
238+
"sparc64": true,
239+
"wasm": true,
240+
}

src/compress/flate/huffman_bit_writer.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -609,10 +609,10 @@ func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode)
609609
var huffOffset *huffmanEncoder
610610

611611
func init() {
612-
w := newHuffmanBitWriter(nil)
613-
w.offsetFreq[0] = 1
612+
offsetFreq := make([]int32, offsetCodeCount)
613+
offsetFreq[0] = 1
614614
huffOffset = newHuffmanEncoder(offsetCodeCount)
615-
huffOffset.generate(w.offsetFreq, 15)
615+
huffOffset.generate(offsetFreq, 15)
616616
}
617617

618618
// writeBlockHuff encodes a block of bytes as either

src/crypto/des/block.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
package des
66

7-
import "encoding/binary"
7+
import (
8+
"encoding/binary"
9+
"sync"
10+
)
811

912
func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) {
1013
b := binary.BigEndian.Uint64(src)
@@ -42,7 +45,8 @@ func decryptBlock(subkeys []uint64, dst, src []byte) {
4245
cryptBlock(subkeys, dst, src, true)
4346
}
4447

45-
// DES Feistel function
48+
// DES Feistel function. feistelBox must be initialized via
49+
// feistelBoxOnce.Do(initFeistelBox) first.
4650
func feistel(l, r uint32, k0, k1 uint64) (lout, rout uint32) {
4751
var t uint32
4852

@@ -77,6 +81,8 @@ func feistel(l, r uint32, k0, k1 uint64) (lout, rout uint32) {
7781
// for sBoxes[s][i][j] << 4*(7-s)
7882
var feistelBox [8][64]uint32
7983

84+
var feistelBoxOnce sync.Once
85+
8086
// general purpose function to perform DES block permutations
8187
func permuteBlock(src uint64, permutation []uint8) (block uint64) {
8288
for position, n := range permutation {
@@ -86,7 +92,7 @@ func permuteBlock(src uint64, permutation []uint8) (block uint64) {
8692
return
8793
}
8894

89-
func init() {
95+
func initFeistelBox() {
9096
for s := range sBoxes {
9197
for i := 0; i < 4; i++ {
9298
for j := 0; j < 16; j++ {
@@ -219,6 +225,8 @@ func ksRotate(in uint32) (out []uint32) {
219225

220226
// creates 16 56-bit subkeys from the original key
221227
func (c *desCipher) generateSubkeys(keyBytes []byte) {
228+
feistelBoxOnce.Do(initFeistelBox)
229+
222230
// apply PC1 permutation to key
223231
key := binary.BigEndian.Uint64(keyBytes)
224232
permutedKey := permuteBlock(key, permutedChoice1[:])

src/flag/flag.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -341,17 +341,15 @@ type Flag struct {
341341

342342
// sortFlags returns the flags as a slice in lexicographical sorted order.
343343
func sortFlags(flags map[string]*Flag) []*Flag {
344-
list := make(sort.StringSlice, len(flags))
344+
result := make([]*Flag, len(flags))
345345
i := 0
346346
for _, f := range flags {
347-
list[i] = f.Name
347+
result[i] = f
348348
i++
349349
}
350-
list.Sort()
351-
result := make([]*Flag, len(list))
352-
for i, name := range list {
353-
result[i] = flags[name]
354-
}
350+
sort.Slice(result, func(i, j int) bool {
351+
return result[i].Name < result[j].Name
352+
})
355353
return result
356354
}
357355

src/net/textproto/reader.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"io/ioutil"
1212
"strconv"
1313
"strings"
14+
"sync"
1415
)
1516

1617
// A Reader implements convenience methods for reading requests
@@ -27,6 +28,7 @@ type Reader struct {
2728
// should be reading from an io.LimitReader or similar Reader to bound
2829
// the size of responses.
2930
func NewReader(r *bufio.Reader) *Reader {
31+
commonHeaderOnce.Do(initCommonHeader)
3032
return &Reader{R: r}
3133
}
3234

@@ -571,6 +573,8 @@ func (r *Reader) upcomingHeaderNewlines() (n int) {
571573
// If s contains a space or invalid header field bytes, it is
572574
// returned without modifications.
573575
func CanonicalMIMEHeaderKey(s string) string {
576+
commonHeaderOnce.Do(initCommonHeader)
577+
574578
// Quick check for canonical encoding.
575579
upper := true
576580
for i := 0; i < len(s); i++ {
@@ -642,9 +646,12 @@ func canonicalMIMEHeaderKey(a []byte) string {
642646
}
643647

644648
// commonHeader interns common header strings.
645-
var commonHeader = make(map[string]string)
649+
var commonHeader map[string]string
650+
651+
var commonHeaderOnce sync.Once
646652

647-
func init() {
653+
func initCommonHeader() {
654+
commonHeader = make(map[string]string)
648655
for _, v := range []string{
649656
"Accept",
650657
"Accept-Charset",

src/net/textproto/reader_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ func TestReadMultiLineError(t *testing.T) {
338338
}
339339

340340
func TestCommonHeaders(t *testing.T) {
341+
commonHeaderOnce.Do(initCommonHeader)
341342
for h := range commonHeader {
342343
if h != CanonicalMIMEHeaderKey(h) {
343344
t.Errorf("Non-canonical header %q in commonHeader", h)

0 commit comments

Comments
 (0)