more perf test

Signed-off-by: Wangchong Zhou <fffonion@gmail.com>
This commit is contained in:
Wangchong Zhou 2018-09-24 15:22:43 -07:00
parent a8dcc589e5
commit a0681a0cd2
No known key found for this signature in database
GPG key ID: B607274584E8D5E5
2 changed files with 308 additions and 186 deletions

View file

@ -168,8 +168,8 @@ func (f *FSM) DumpFSM(w io.Writer) {
// TestIfNeedBacktracking test if backtrack is needed for current FSM
func (f *FSM) TestIfNeedBacktracking(mappings []string) bool {
needBacktrack := false
// rule A and B that has same length and
// A one has * in rules but is not a superset of B makes it needed for backtracking
// A has * in rules there's other transisitions at the same state
// this makes A the cause of backtracking
ruleByLength := make(map[int][]string)
ruleREByLength := make(map[int][]*regexp.Regexp)
@ -193,14 +193,31 @@ func (f *FSM) TestIfNeedBacktracking(mappings []string) bool {
continue
}
rulesRE := ruleREByLength[l]
// for each rule r1 in rules that has * inside, check if r1 is the superset of any rules
// if not then r1 is a rule that leads to backtrack
for i1, r1 := range rules {
currentRuleNeedBacktrack := true
currentRuleNeedBacktrack := false
re1 := rulesRE[i1]
if re1 == nil || strings.Index(r1, "*") == -1 {
continue
}
// if a rule is A.B.C.*.E.*, is there a rule A.B.C.D.x.x or A.B.C.*.E.F? (x is any string or *)
for index := 0; index < len(r1); index++ {
if r1[index] != '*' {
continue
}
reStr := strings.Replace(r1[:index], ".", "\\.", -1)
reStr = strings.Replace(reStr, "*", "\\*", -1)
re := regexp.MustCompile("^" + reStr)
for i2, r2 := range rules {
if i2 == i1 {
continue
}
if len(re.FindStringSubmatchIndex(r2)) > 0 {
currentRuleNeedBacktrack = true
break
}
}
}
for i2, r2 := range rules {
if i2 != i1 && len(re1.FindStringSubmatchIndex(r2)) > 0 {
// log if we care about ordering and the superset occurs before
@ -212,13 +229,17 @@ func (f *FSM) TestIfNeedBacktracking(mappings []string) bool {
}
}
for i2, re2 := range rulesRE {
// especially, if r1 is a subset of other rule, we don't need backtrack
if i2 == i1 || re2 == nil {
continue
}
// if r1 is a subset of other rule, we don't need backtrack
// because either we turned on ordering
// or we disabled ordering and can't match it even with backtrack
if i2 != i1 && re2 != nil && len(re2.FindStringSubmatchIndex(r1)) > 0 {
if len(re2.FindStringSubmatchIndex(r1)) > 0 {
currentRuleNeedBacktrack = false
}
}
if currentRuleNeedBacktrack {
log.Warnf("backtracking required because of match \"%s\", "+
"matching performance may be degraded\n", r1)

View file

@ -608,7 +608,44 @@ mappings:
}
}
func duplicateRules(count int, template string) string {
rules := ""
for i := 0; i < count; i++ {
rules += fmt.Sprintf(template, i)
}
return rules
}
func TestPerformance(t *testing.T) {
sampleRulesAmount := 100
ruleTemplateSingleMatchGlob := `
- match: metric%d.*
name: "metric_single"
labels:
name: "$1"
`
ruleTemplateSingleMatchRegex := `
- match: metric%d\.([^.]*)
name: "metric_single"
labels:
name: "$1"
`
ruleTemplateMultipleMatchGlob := `
- match: metric%d.*.*.*.*.*.*.*.*.*.*.*.*
name: "metric_multi"
labels:
name: "$1-$2-$3.$4-$5-$6.$7-$8-$9.$10-$11-$12"
`
ruleTemplateMultipleMatchRegex := `
- match: metric%d\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)
name: "metric_multi"
labels:
name: "$1-$2-$3.$4-$5-$6.$7-$8-$9.$10-$11-$12"
`
scenarios := []struct {
name string
config string
@ -657,50 +694,10 @@ mappings:
job: "-"
`,
mappings: mappings{
"test.dispatcher.FooProcessor.send.succeeded": {
name: "dispatch_events",
labels: map[string]string{
"processor": "FooProcessor",
"action": "send",
"result": "succeeded",
"job": "test_dispatcher",
},
},
"test.my-dispatch-host01.name.dispatcher.FooProcessor.send.succeeded": {
name: "host_dispatch_events",
labels: map[string]string{
"processor": "FooProcessor",
"action": "send",
"result": "succeeded",
"job": "test_dispatcher",
},
},
"request_time.get/threads/1/posts.200.00000000.nonversioned.discussions.a11bbcdf0ac64ec243658dc64b7100fb.172.20.0.1.12ba97b7eaa1a50001000001.": {
name: "tyk_http_request",
labels: map[string]string{
"method_and_path": "get/threads/1/posts",
"response_code": "200",
"apikey": "00000000",
"apiversion": "nonversioned",
"apiname": "discussions",
"apiid": "a11bbcdf0ac64ec243658dc64b7100fb",
"ipv4_t1": "172",
"ipv4_t2": "20",
"ipv4_t3": "0",
"ipv4_t4": "1",
"orgid": "12ba97b7eaa1a50001000001",
"oauthid": "",
},
},
"foo.bar": {
name: "catchall",
labels: map[string]string{
"first": "foo",
"second": "bar",
"third": "",
"job": "-",
},
},
"test.dispatcher.FooProcessor.send.succeeded": {},
"test.my-dispatch-host01.name.dispatcher.FooProcessor.send.succeeded": {},
"request_time.get/threads/1/posts.200.00000000.nonversioned.discussions.a11bbcdf0ac64ec243658dc64b7100fb.172.20.0.1.12ba97b7eaa1a50001000001.": {},
"foo.bar": {},
"foo.bar.baz": {},
},
},
@ -748,50 +745,10 @@ mappings:
job: "-"
`,
mappings: mappings{
"test.dispatcher.FooProcessor.send.succeeded": {
name: "dispatch_events",
labels: map[string]string{
"processor": "FooProcessor",
"action": "send",
"result": "succeeded",
"job": "test_dispatcher",
},
},
"test.my-dispatch-host01.name.dispatcher.FooProcessor.send.succeeded": {
name: "host_dispatch_events",
labels: map[string]string{
"processor": "FooProcessor",
"action": "send",
"result": "succeeded",
"job": "test_dispatcher",
},
},
"request_time.get/threads/1/posts.200.00000000.nonversioned.discussions.a11bbcdf0ac64ec243658dc64b7100fb.172.20.0.1.12ba97b7eaa1a50001000001.": {
name: "tyk_http_request",
labels: map[string]string{
"method_and_path": "get/threads/1/posts",
"response_code": "200",
"apikey": "00000000",
"apiversion": "nonversioned",
"apiname": "discussions",
"apiid": "a11bbcdf0ac64ec243658dc64b7100fb",
"ipv4_t1": "172",
"ipv4_t2": "20",
"ipv4_t3": "0",
"ipv4_t4": "1",
"orgid": "12ba97b7eaa1a50001000001",
"oauthid": "",
},
},
"foo.bar": {
name: "catchall",
labels: map[string]string{
"first": "foo",
"second": "bar",
"third": "",
"job": "-",
},
},
"test.dispatcher.FooProcessor.send.succeeded": {},
"test.my-dispatch-host01.name.dispatcher.FooProcessor.send.succeeded": {},
"request_time.get/threads/1/posts.200.00000000.nonversioned.discussions.a11bbcdf0ac64ec243658dc64b7100fb.172.20.0.1.12ba97b7eaa1a50001000001.": {},
"foo.bar": {},
"foo.bar.baz": {},
},
},
@ -846,50 +803,10 @@ mappings:
job: "-"
`,
mappings: mappings{
"test.dispatcher.FooProcessor.send.succeeded": {
name: "dispatch_events",
labels: map[string]string{
"processor": "FooProcessor",
"action": "send",
"result": "succeeded",
"job": "test_dispatcher",
},
},
"test.my-dispatch-host01.name.dispatcher.FooProcessor.send.succeeded": {
name: "host_dispatch_events",
labels: map[string]string{
"processor": "FooProcessor",
"action": "send",
"result": "succeeded",
"job": "test_dispatcher",
},
},
"request_time.get/threads/1/posts.200.00000000.nonversioned.discussions.a11bbcdf0ac64ec243658dc64b7100fb.172.20.0.1.12ba97b7eaa1a50001000001.": {
name: "tyk_http_request",
labels: map[string]string{
"method_and_path": "get/threads/1/posts",
"response_code": "200",
"apikey": "00000000",
"apiversion": "nonversioned",
"apiname": "discussions",
"apiid": "a11bbcdf0ac64ec243658dc64b7100fb",
"ipv4_t1": "172",
"ipv4_t2": "20",
"ipv4_t3": "0",
"ipv4_t4": "1",
"orgid": "12ba97b7eaa1a50001000001",
"oauthid": "",
},
},
"foo.bar": {
name: "catchall",
labels: map[string]string{
"first": "foo",
"second": "bar",
"third": "",
"job": "-",
},
},
"test.dispatcher.FooProcessor.send.succeeded": {},
"test.my-dispatch-host01.name.dispatcher.FooProcessor.send.succeeded": {},
"request_time.get/threads/1/posts.200.00000000.nonversioned.discussions.a11bbcdf0ac64ec243658dc64b7100fb.172.20.0.1.12ba97b7eaa1a50001000001.": {},
"foo.bar": {},
"foo.bar.baz": {},
},
},
@ -937,57 +854,241 @@ mappings:
job: "-"
`,
mappings: mappings{
"test.dispatcher.FooProcessor.send.succeeded": {
name: "dispatch_events",
labels: map[string]string{
"processor": "FooProcessor",
"action": "send",
"result": "succeeded",
"job": "test_dispatcher",
},
},
"test.my-dispatch-host01.name.dispatcher.FooProcessor.send.succeeded": {
name: "host_dispatch_events",
labels: map[string]string{
"processor": "FooProcessor",
"action": "send",
"result": "succeeded",
"job": "test_dispatcher",
},
},
"request_time.get/threads/1/posts.200.00000000.nonversioned.discussions.a11bbcdf0ac64ec243658dc64b7100fb.172.20.0.1.12ba97b7eaa1a50001000001.": {
name: "tyk_http_request",
labels: map[string]string{
"method_and_path": "get/threads/1/posts",
"response_code": "200",
"apikey": "00000000",
"apiversion": "nonversioned",
"apiname": "discussions",
"apiid": "a11bbcdf0ac64ec243658dc64b7100fb",
"ipv4_t1": "172",
"ipv4_t2": "20",
"ipv4_t3": "0",
"ipv4_t4": "1",
"orgid": "12ba97b7eaa1a50001000001",
"oauthid": "",
},
},
"foo.bar": {
name: "catchall",
labels: map[string]string{
"first": "foo",
"second": "bar",
"third": "",
"job": "-",
},
},
"test.dispatcher.FooProcessor.send.succeeded": {},
"test.my-dispatch-host01.name.dispatcher.FooProcessor.send.succeeded": {},
"request_time.get/threads/1/posts.200.00000000.nonversioned.discussions.a11bbcdf0ac64ec243658dc64b7100fb.172.20.0.1.12ba97b7eaa1a50001000001.": {},
"foo.bar": {},
"foo.bar.baz": {},
},
},
{},
{
name: "glob single match ",
config: `---
mappings:
- match: metric.*
name: "metric_one"
labels:
name: "$1"
`,
mappings: mappings{
"metric.aaa": {},
"metric.bbb": {},
},
},
{
name: "regex single match",
config: `---
mappings:
- match: metric\.([^.]*)
name: "metric_one"
match_type: regex
labels:
name: "$1"
`,
mappings: mappings{
"metric.aaa": {},
"metric.bbb": {},
},
},
{},
{
name: "glob multiple captures",
config: `---
mappings:
- match: metric.*.*.*.*.*.*.*.*.*.*.*.*
name: "metric_multi"
labels:
name: "$1-$2-$3.$4-$5-$6.$7-$8-$9.$10-$11-$12"
`,
mappings: mappings{
"metric.a.b.c.d.e.f.g.h.i.j.k.l": {},
},
},
{
name: "regex multiple captures",
config: `---
mappings:
- match: metric\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)
name: "metric_multi"
match_type: regex
labels:
name: "$1-$2-$3.$4-$5-$6.$7-$8-$9.$10-$11-$12"
`,
mappings: mappings{
"metric.a.b.c.d.e.f.g.h.i.j.k.l": {},
},
},
{
name: "glob multiple captures no format",
config: `---
mappings:
- match: metric.*.*.*.*.*.*.*.*.*.*.*.*
name: "metric_multi"
labels:
name: "$1"
`,
mappings: mappings{
"metric.a.b.c.d.e.f.g.h.i.j.k.l": {},
},
},
{
name: "regex multiple captures no expansion",
config: `---
mappings:
- match: metric\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)
name: "metric_multi"
match_type: regex
labels:
name: "$1"
`,
mappings: mappings{
"metric.a.b.c.d.e.f.g.h.i.j.k.l": {},
},
},
{},
{
name: "glob multiple captures in different labels",
config: `---
mappings:
- match: metric.*.*.*.*.*.*.*.*.*.*.*.*
name: "metric_multi"
labels:
label1: "$1"
label2: "$2"
label3: "$3"
label4: "$4"
label5: "$5"
label6: "$6"
label7: "$7"
label8: "$8"
label9: "$9"
label10: "$10"
label11: "$11"
label12: "$12"
`,
mappings: mappings{
"metric.a.b.c.d.e.f.g.h.i.j.k.l": {},
},
},
{
name: "regex multiple captures",
config: `---
mappings:
- match: metric\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)\.([^.]*)
name: "metric_multi"
match_type: regex
labels:
label1: "$1"
label2: "$2"
label3: "$3"
label4: "$4"
label5: "$5"
label6: "$6"
label7: "$7"
label8: "$8"
label9: "$9"
label10: "$10"
label11: "$11"
label12: "$12"
`,
mappings: mappings{
"metric.a.b.c.d.e.f.g.h.i.j.k.l": {},
},
},
{},
{
name: "glob 100 rules",
config: `---
mappings:` + duplicateRules(sampleRulesAmount, ruleTemplateSingleMatchGlob),
mappings: mappings{
"metric100.a": {},
},
},
{
name: "glob 100 rules, no match",
config: `---
mappings:` + duplicateRules(sampleRulesAmount, ruleTemplateSingleMatchGlob),
mappings: mappings{
"metricnomatchy.a": {},
},
},
{
name: "glob 100 rules without ordering, no match",
config: `---
defaults:
glob_disable_ordering: true
mappings:` + duplicateRules(sampleRulesAmount, ruleTemplateSingleMatchGlob),
mappings: mappings{
"metricnomatchy.a": {},
},
},
{
name: "regex 100 rules, average case",
config: `---
defaults:
match_type: regex
mappings:` + duplicateRules(sampleRulesAmount, ruleTemplateSingleMatchRegex),
mappings: mappings{
fmt.Sprintf("metric%d.a", sampleRulesAmount/2): {}, // average match position
},
},
{
name: "regex 100 rules, worst case",
config: `---
defaults:
match_type: regex
mappings:` + duplicateRules(sampleRulesAmount, ruleTemplateSingleMatchRegex),
mappings: mappings{
"metric100.a": {},
},
},
{},
{
name: "glob 100 rules, multiple captures",
config: `---
mappings:` + duplicateRules(sampleRulesAmount, ruleTemplateMultipleMatchGlob),
mappings: mappings{
"metric50.a.b.c.d.e.f.g.h.i.j.k.l": {},
},
},
{
name: "regex 100 rules, multiple captures, average case",
config: `---
defaults:
match_type: regex
mappings:` + duplicateRules(sampleRulesAmount, ruleTemplateMultipleMatchRegex),
mappings: mappings{
fmt.Sprintf("metric%d.a.b.c.d.e.f.g.h.i.j.k.l", sampleRulesAmount/2): {}, // average match position
},
},
{},
{
name: "glob 10 rules",
config: `---
mappings:` + duplicateRules(sampleRulesAmount, ruleTemplateSingleMatchGlob),
mappings: mappings{
"metric50.a": {},
},
},
{
name: "regex 10 rules average case",
config: `---
defaults:
match_type: regex
mappings:` + duplicateRules(sampleRulesAmount, ruleTemplateSingleMatchRegex),
mappings: mappings{
fmt.Sprintf("metric%d.a", sampleRulesAmount/2): {}, // average match position
},
},
}
mapper := MetricMapper{}
for i, scenario := range scenarios {
if len(scenario.config) == 0 {
fmt.Println("--------------------------------")
continue
}
mapper := MetricMapper{}
err := mapper.InitFromYAMLString(scenario.config)
if err != nil && !scenario.configBad {
t.Fatalf("%d. Config load error: %s %s", i, scenario.config, err)
@ -998,12 +1099,12 @@ mappings:
var dummyMetricType MetricType = ""
start := time.Now()
for j := 1; j < 100000; j++ {
for j := 1; j < 10000; j++ {
for metric, _ := range scenario.mappings {
mapper.GetMapping(metric, dummyMetricType)
}
}
fmt.Printf("finished 100000 iterations for \"%s\" in %v\n", scenario.name, time.Now().Sub(start))
fmt.Printf("finished 10000 iterations for \"%s\" in %v\n", scenario.name, time.Now().Sub(start))
}
}