Pre-allocate ninjaString slices
Naively pre-allocate ninjaString slices by counting $ characters as
an estimate of how many variables will be needed. Saves 5% cpu time
on one workload.
Change-Id: Ib3a41df559d728b2db047f6dbbf9eb06d7045303
diff --git a/ninja_strings.go b/ninja_strings.go
index 3c3b444..d640c87 100644
--- a/ninja_strings.go
+++ b/ninja_strings.go
@@ -80,7 +80,12 @@
// occurrences are expected to be variables or $$) and returns a list of the
// variable names that the string references.
func parseNinjaString(scope scope, str string) (*ninjaString, error) {
- result := &ninjaString{}
+ // naively pre-allocate slices by counting $ signs
+ n := strings.Count(str, "$")
+ result := &ninjaString{
+ strings: make([]string, 0, n+1),
+ variables: make([]Variable, 0, n),
+ }
parseState := &parseState{
scope: scope,
diff --git a/ninja_strings_test.go b/ninja_strings_test.go
index b35c8b4..695f14c 100644
--- a/ninja_strings_test.go
+++ b/ninja_strings_test.go
@@ -66,6 +66,11 @@
strs: []string{"","$$"},
},
{
+ input: "foo bar",
+ vars: nil,
+ strs: []string{"foo bar"},
+ },
+ {
input: "foo $ bar",
err: "invalid character after '$' at byte offset 5",
},
@@ -90,7 +95,7 @@
func TestParseNinjaString(t *testing.T) {
for _, testCase := range ninjaParseTestCases {
scope := newLocalScope(nil, "namespace")
- var expectedVars []Variable
+ expectedVars := []Variable{}
for _, varName := range testCase.vars {
v, err := scope.LookupVariable(varName)
if err != nil {
@@ -107,7 +112,7 @@
if !reflect.DeepEqual(output.variables, expectedVars) {
t.Errorf("incorrect variable list:")
t.Errorf(" input: %q", testCase.input)
- t.Errorf(" expected: %#v", testCase.vars)
+ t.Errorf(" expected: %#v", expectedVars)
t.Errorf(" got: %#v", output.variables)
}
if !reflect.DeepEqual(output.strings, testCase.strs) {