Skip to content

Commit bbcf96f

Browse files
kolyshkinthaJeztah
andcommitted
libct/cg/devices: stop using regex
Looking into data generated by setting GODEBUG="inittrace=1" I have noticed this line: init github.com/opencontainers/runc/libcontainer/cgroups/devices @1.2 ms, 0.020 ms clock, 10512 bytes, 133 allocs This is the leader for both bytes and allocs among the packages from this repo, and all of it is caused by a single regex: > var devicesListRegexp = regexp.MustCompile(`^([abc])\s+(\d+|\*):(\d+|\*)\s+([rwm]+)$`) It seems that the same parsing can be done without relying on a regular expression, no decrease in readability, and 2x faster (according to the benchmark added), and also makes runc start slightly faster and leaner. Before: BenchmarkParseLine-4 176240 6768 ns/op 6576 B/op 64 allocs/op After: BenchmarkParseLine-4 322441 3535 ns/op 5520 B/op 53 allocs/op [v2: single split with SplitFunc; fix a typo in error message] [v3: rebase after 3159 merge; re-ran benchmarks (results are similar)] Co-authored-by: Sebastiaan van Stijn <[email protected]> Signed-off-by: Kir Kolyshkin <[email protected]>
1 parent 34df203 commit bbcf96f

File tree

2 files changed

+42
-13
lines changed

2 files changed

+42
-13
lines changed

libcontainer/cgroups/devices/devices_emulator.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@ package devices
2222

2323
import (
2424
"bufio"
25-
"errors"
2625
"fmt"
2726
"io"
28-
"regexp"
2927
"sort"
3028
"strconv"
29+
"strings"
3130

3231
"github.com/opencontainers/runc/libcontainer/devices"
3332
)
@@ -79,19 +78,21 @@ func (e *Emulator) IsAllowAll() bool {
7978
return e.IsBlacklist() && len(e.rules) == 0
8079
}
8180

82-
var devicesListRegexp = regexp.MustCompile(`^([abc])\s+(\d+|\*):(\d+|\*)\s+([rwm]+)$`)
83-
8481
func parseLine(line string) (*deviceRule, error) {
85-
matches := devicesListRegexp.FindStringSubmatch(line)
86-
if matches == nil {
87-
return nil, errors.New("line doesn't match devices.list format")
82+
// Input: node major:minor perms.
83+
fields := strings.FieldsFunc(line, func(r rune) bool {
84+
return r == ' ' || r == ':'
85+
})
86+
if len(fields) != 4 {
87+
return nil, fmt.Errorf("malformed devices.list rule %s", line)
8888
}
89+
8990
var (
9091
rule deviceRule
91-
node = devices.Type(matches[1])
92-
major = matches[2]
93-
minor = matches[3]
94-
perms = matches[4]
92+
node = devices.Type(fields[0])
93+
major = fields[1]
94+
minor = fields[2]
95+
perms = fields[3]
9596
)
9697

9798
// Parse the node type.
@@ -105,7 +106,6 @@ func parseLine(line string) (*deviceRule, error) {
105106
case devices.BlockDevice, devices.CharDevice:
106107
rule.meta.node = node
107108
default:
108-
// Should never happen!
109109
return nil, fmt.Errorf("unknown device type %q", node)
110110
}
111111

@@ -134,7 +134,6 @@ func parseLine(line string) (*deviceRule, error) {
134134
// Parse the access permissions.
135135
rule.perms = devices.Permissions(perms)
136136
if !rule.perms.IsValid() || rule.perms.IsEmpty() {
137-
// Should never happen!
138137
return nil, fmt.Errorf("parse access mode: contained unknown modes or is empty: %q", perms)
139138
}
140139

libcontainer/cgroups/devices/devices_emulator_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
package devices
2020

2121
import (
22+
"bufio"
2223
"bytes"
2324
"reflect"
25+
"strings"
2426
"testing"
2527

2628
"github.com/opencontainers/runc/libcontainer/devices"
@@ -1112,3 +1114,31 @@ func TestDeviceEmulatorTransitionFromBlacklist(t *testing.T) {
11121114
func TestDeviceEmulatorTransitionFromWhitelist(t *testing.T) {
11131115
testDeviceEmulatorTransition(t, false)
11141116
}
1117+
1118+
func BenchmarkParseLine(b *testing.B) {
1119+
list := `c *:* m
1120+
b *:* m
1121+
c 1:3 rwm
1122+
c 1:5 rwm
1123+
c 1:7 rwm
1124+
c 1:8 rwm
1125+
c 1:9 rwm
1126+
c 5:0 rwm
1127+
c 5:2 rwm
1128+
c 136:* rwm
1129+
c 10:200 rwm`
1130+
1131+
var r *deviceRule
1132+
var err error
1133+
for i := 0; i < b.N; i++ {
1134+
s := bufio.NewScanner(strings.NewReader(list))
1135+
for s.Scan() {
1136+
line := s.Text()
1137+
r, err = parseLine(line)
1138+
}
1139+
if err := s.Err(); err != nil {
1140+
b.Fatal(err)
1141+
}
1142+
}
1143+
b.Logf("rule: %v, err: %v", r, err)
1144+
}

0 commit comments

Comments
 (0)