From 9bb3eebd9ca45ae787360772da1badbe4cbc43eb Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Tue, 7 Sep 2021 13:47:55 -0700 Subject: [PATCH] readConfig,loadLabels: stop using regex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using a globally initialized regex incurs some overhead during package initialization, which is noticeable for all other packages importing it. Besides, there's no need to use regex to perform key=value split. Rewrite both functions without using regex, and add benchmarks, which show that the new code is faster: name old time/op new time/op delta ReadConfig-4 4.86µs ± 1% 3.46µs ± 2% -28.82% (p=0.002 n=6+6) LoadLabels-4 12.7µs ± 3% 6.8µs ± 1% -46.24% (p=0.002 n=6+6) name old alloc/op new alloc/op delta ReadConfig-4 5.08kB ± 0% 4.35kB ± 0% -14.33% (p=0.002 n=6+6) LoadLabels-4 6.49kB ± 0% 6.10kB ± 0% -6.07% (p=0.000 n=6+5) name old allocs/op new allocs/op delta ReadConfig-4 20.0 ± 0% 8.0 ± 0% -60.00% (p=0.002 n=6+6) LoadLabels-4 45.0 ± 0% 45.0 ± 0% ~ (all equal) Besides, the init overhead is eliminated: Before: init github.com/opencontainers/selinux/go-selinux @2.4 ms, 0.088 ms clock, 5016 bytes, 72 allocs After: init github.com/opencontainers/selinux/go-selinux @2.9 ms, 0.001 ms clock, 160 bytes, 8 allocs Signed-off-by: Kir Kolyshkin --- go-selinux/selinux_linux.go | 25 +++++++++++++------------ go-selinux/selinux_linux_test.go | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/go-selinux/selinux_linux.go b/go-selinux/selinux_linux.go index bfaefc0..e2c0fc5 100644 --- a/go-selinux/selinux_linux.go +++ b/go-selinux/selinux_linux.go @@ -12,7 +12,6 @@ import ( "os" "path" "path/filepath" - "regexp" "strconv" "strings" "sync" @@ -68,7 +67,6 @@ const ( ) var ( - assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`) readOnlyFileLabel string state = selinuxState{ mcsList: make(map[string]bool), @@ -237,7 +235,7 @@ func readConfig(target string) string { scanner := bufio.NewScanner(in) for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) + line := bytes.TrimSpace(scanner.Bytes()) if len(line) == 0 { // Skip blank lines continue @@ -246,11 +244,12 @@ func readConfig(target string) string { // Skip comments continue } - if groups := assignRegex.FindStringSubmatch(line); groups != nil { - key, val := strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) - if key == target { - return strings.Trim(val, "\"") - } + fields := bytes.SplitN(line, []byte{'='}, 2) + if len(fields) != 2 { + continue + } + if bytes.Equal(fields[0], []byte(target)) { + return string(bytes.Trim(fields[1], `"`)) } } return "" @@ -920,7 +919,7 @@ func loadLabels() { scanner := bufio.NewScanner(in) for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) + line := bytes.TrimSpace(scanner.Bytes()) if len(line) == 0 { // Skip blank lines continue @@ -929,10 +928,12 @@ func loadLabels() { // Skip comments continue } - if groups := assignRegex.FindStringSubmatch(line); groups != nil { - key, val := strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) - labels[key] = strings.Trim(val, "\"") + fields := bytes.SplitN(line, []byte{'='}, 2) + if len(fields) != 2 { + continue } + key, val := bytes.TrimSpace(fields[0]), bytes.TrimSpace(fields[1]) + labels[string(key)] = string(bytes.Trim(val, `"`)) } con, _ := NewContext(labels["file"]) diff --git a/go-selinux/selinux_linux_test.go b/go-selinux/selinux_linux_test.go index af315d6..1d4ef27 100644 --- a/go-selinux/selinux_linux_test.go +++ b/go-selinux/selinux_linux_test.go @@ -507,3 +507,17 @@ func BenchmarkChcon(b *testing.B) { } } } + +func BenchmarkReadConfig(b *testing.B) { + str := "" + for n := 0; n < b.N; n++ { + str = readConfig(selinuxTypeTag) + } + b.Log(str) +} + +func BenchmarkLoadLabels(b *testing.B) { + for n := 0; n < b.N; n++ { + loadLabels() + } +}