Skip to content

Commit dd1402a

Browse files
omosurteepark
authored andcommitted
Fix performance for gatheInfo func, move regexp to global var for once regexp compilation. (kelseyhightower#115)
Benchmark result: goos: linux goarch: amd64 pkg: github.com/kelseyhightower/envconfig benchmark old ns/op new ns/op delta BenchmarkGatherInfo-4 756307 535401 -29.21% benchmark old allocs new allocs delta BenchmarkGatherInfo-4 349 251 -28.08% benchmark old bytes new bytes delta BenchmarkGatherInfo-4 64467 15425 -76.07%
1 parent b2c5c87 commit dd1402a

File tree

2 files changed

+29
-3
lines changed

2 files changed

+29
-3
lines changed

envconfig.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919
// ErrInvalidSpecification indicates that a specification is of the wrong type.
2020
var ErrInvalidSpecification = errors.New("specification must be a struct pointer")
2121

22+
var gatherRegexp = regexp.MustCompile("([^A-Z]+|[A-Z][^A-Z]+|[A-Z]+)")
23+
2224
// A ParseError occurs when an environment variable cannot be converted to
2325
// the type required by a struct field during assignment.
2426
type ParseError struct {
@@ -56,7 +58,6 @@ type varInfo struct {
5658

5759
// GatherInfo gathers information about the specified struct
5860
func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) {
59-
expr := regexp.MustCompile("([^A-Z]+|[A-Z][^A-Z]+|[A-Z]+)")
6061
s := reflect.ValueOf(spec)
6162

6263
if s.Kind() != reflect.Ptr {
@@ -102,7 +103,7 @@ func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) {
102103

103104
// Best effort to un-pick camel casing as separate words
104105
if isTrue(ftype.Tag.Get("split_words")) {
105-
words := expr.FindAllStringSubmatch(ftype.Name, -1)
106+
words := gatherRegexp.FindAllStringSubmatch(ftype.Name, -1)
106107
if len(words) > 0 {
107108
var name []string
108109
for _, words := range words {
@@ -202,7 +203,7 @@ func Process(prefix string, spec interface{}) error {
202203
continue
203204
}
204205

205-
err := processField(value, info.Field)
206+
err = processField(value, info.Field)
206207
if err != nil {
207208
return &ParseError{
208209
KeyName: info.Key,

envconfig_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,3 +802,28 @@ func (ss *setterStruct) Set(value string) error {
802802
ss.Inner = fmt.Sprintf("setterstruct{%q}", value)
803803
return nil
804804
}
805+
806+
func BenchmarkGatherInfo(b *testing.B) {
807+
os.Clearenv()
808+
os.Setenv("ENV_CONFIG_DEBUG", "true")
809+
os.Setenv("ENV_CONFIG_PORT", "8080")
810+
os.Setenv("ENV_CONFIG_RATE", "0.5")
811+
os.Setenv("ENV_CONFIG_USER", "Kelsey")
812+
os.Setenv("ENV_CONFIG_TIMEOUT", "2m")
813+
os.Setenv("ENV_CONFIG_ADMINUSERS", "John,Adam,Will")
814+
os.Setenv("ENV_CONFIG_MAGICNUMBERS", "5,10,20")
815+
os.Setenv("ENV_CONFIG_COLORCODES", "red:1,green:2,blue:3")
816+
os.Setenv("SERVICE_HOST", "127.0.0.1")
817+
os.Setenv("ENV_CONFIG_TTL", "30")
818+
os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
819+
os.Setenv("ENV_CONFIG_IGNORED", "was-not-ignored")
820+
os.Setenv("ENV_CONFIG_OUTER_INNER", "iamnested")
821+
os.Setenv("ENV_CONFIG_AFTERNESTED", "after")
822+
os.Setenv("ENV_CONFIG_HONOR", "honor")
823+
os.Setenv("ENV_CONFIG_DATETIME", "2016-08-16T18:57:05Z")
824+
os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_AUTO_SPLIT", "24")
825+
for i := 0; i < b.N; i++ {
826+
var s Specification
827+
gatherInfo("env_config", &s)
828+
}
829+
}

0 commit comments

Comments
 (0)