From c89afc5939ff8bf3dc3be58ef57bf9b9e410790e Mon Sep 17 00:00:00 2001 From: Walter Schulze Date: Tue, 15 Aug 2017 19:32:40 +0200 Subject: [PATCH 1/3] benchmark against goplayground --- parser/reflect/reflect.go | 10 +- relapse/mem/benchreflect_test.go | 284 +++++++++++++++++++++++++++++++ 2 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 relapse/mem/benchreflect_test.go diff --git a/parser/reflect/reflect.go b/parser/reflect/reflect.go index ea52340..9e68dbd 100644 --- a/parser/reflect/reflect.go +++ b/parser/reflect/reflect.go @@ -16,9 +16,10 @@ package reflect import ( - "github.com/katydid/katydid/parser" "io" "reflect" + + "github.com/katydid/katydid/parser" ) type state struct { @@ -74,6 +75,7 @@ type ReflectParser interface { parser.Interface //Init initialises the parser with a value of reflected go structure. Init(value reflect.Value) ReflectParser + Reset() error } //NewReflectParser returns a new reflect parser. @@ -86,6 +88,12 @@ func (s *reflectParser) Init(value reflect.Value) ReflectParser { return s } +func (s *reflectParser) Reset() error { + s.stack = s.stack[:0] + s.state = newState(s.state.value) + return nil +} + func (s *reflectParser) Next() error { if s.field >= s.maxField { return io.EOF diff --git a/relapse/mem/benchreflect_test.go b/relapse/mem/benchreflect_test.go new file mode 100644 index 0000000..38fe4f8 --- /dev/null +++ b/relapse/mem/benchreflect_test.go @@ -0,0 +1,284 @@ +package mem_test + +import ( + "reflect" + "testing" + + "github.com/katydid/katydid/relapse/testsuite" + + parser "github.com/katydid/katydid/parser/reflect" + "github.com/katydid/katydid/relapse" +) + +type TestStruct struct { + String string `validate:"required" json:"StringVal"` +} + +// 219 ns/op +// func BenchmarkStructLevelValidationSuccess(b *testing.B) { + +// validate := New() +// validate.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{}) + +// tst := TestStruct{ +// String: "good value", +// } + +// b.ResetTimer() +// for n := 0; n < b.N; n++ { +// validate.Struct(tst) +// } +// } + +// 132 ns/op +func BenchmarkGoPlaygroundStructLevelValidationSuccess(b *testing.B) { + tst := TestStruct{ + String: "good value", + } + s := ".String:*" + g, err := relapse.Parse(s) + if err != nil { + b.Fatal(err) + } + p := parser.NewReflectParser().Init(reflect.ValueOf(tst)) + bench(b, g, []testsuite.ResetParser{p}, true) +} + +// 263 ns/op +// func BenchmarkStructSimpleSuccess(b *testing.B) { + +// validate := New() + +// type Foo struct { +// StringValue string `validate:"min=5,max=10"` +// IntValue int `validate:"min=5,max=10"` +// } + +// validFoo := &Foo{StringValue: "Foobar", IntValue: 7} + +// b.ResetTimer() +// for n := 0; n < b.N; n++ { +// validate.Struct(validFoo) +// } +// } + +// 155 ns/op +func BenchmarkGoPlaygroundStructSimpleSuccess(b *testing.B) { + type Foo struct { + StringValue string `validate:"min=5,max=10"` + IntValue int `validate:"min=5,max=10"` + } + validFoo := &Foo{StringValue: "Foobar", IntValue: 7} + s := `{ + (StringValue->and(ge(length($string), 5), le(length($string), 10)))? ; + (IntValue->and(ge($int, 5), le($int, 10)))? + }` + g, err := relapse.Parse(s) + if err != nil { + b.Fatal(err) + } + p := parser.NewReflectParser().Init(reflect.ValueOf(validFoo)) + bench(b, g, []testsuite.ResetParser{p}, true) +} + +type TestString struct { + BlankTag string `validate:""` + Required string `validate:"required"` + Len string `validate:"len=10"` + Min string `validate:"min=1"` + Max string `validate:"max=10"` + MinMax string `validate:"min=1,max=10"` + Lt string `validate:"lt=10"` + Lte string `validate:"lte=10"` + Gt string `validate:"gt=10"` + Gte string `validate:"gte=10"` + OmitEmpty string `validate:"omitempty,min=1,max=10"` + Sub *SubTest + SubIgnore *SubTest `validate:"-"` + Anonymous struct { + A string `validate:"required"` + } + Iface I +} + +type SubTest struct { + Test string `validate:"required"` +} + +type TestInterface struct { + Iface I +} + +type I interface { + Foo() string +} + +type Impl struct { + F string `validate:"len=3"` +} + +func (i *Impl) Foo() string { + return i.F +} + +// 1504 ns/op +// func BenchmarkStructComplexSuccess(b *testing.B) { + +// validate := New() + +// tSuccess := &TestString{ +// Required: "Required", +// Len: "length==10", +// Min: "min=1", +// Max: "1234567890", +// MinMax: "12345", +// Lt: "012345678", +// Lte: "0123456789", +// Gt: "01234567890", +// Gte: "0123456789", +// OmitEmpty: "", +// Sub: &SubTest{ +// Test: "1", +// }, +// SubIgnore: &SubTest{ +// Test: "", +// }, +// Anonymous: struct { +// A string `validate:"required"` +// }{ +// A: "1", +// }, +// Iface: &Impl{ +// F: "123", +// }, +// } + +// b.ResetTimer() +// for n := 0; n < b.N; n++ { +// validate.Struct(tSuccess) +// } +// } + +// 444 ns/op +func BenchmarkGoPlaygroundStructComplexSuccess(b *testing.B) { + tSuccess := &TestString{ + Required: "Required", + Len: "length==10", + Min: "min=1", + Max: "1234567890", + MinMax: "12345", + Lt: "012345678", + Lte: "0123456789", + Gt: "01234567890", + Gte: "0123456789", + OmitEmpty: "", + Sub: &SubTest{ + Test: "1", + }, + SubIgnore: &SubTest{ + Test: "", + }, + } + s := `#main = { + Required->gt(length($string), 0) ; + (Len->eq(length($string), 10))? ; + (Min->ge(length($string), 1))? ; + (Max->le(length($string), 10))? ; + (MinMax->and(ge(length($string), 1), le(length($string), 10)))? ; + (Lt->lt(length($string), 10))? ; + (Lte->le(length($string), 10))? ; + (Gt->gt(length($string), 10))? ; + (Gte->ge(length($string), 10))? ; + (OmitEmpty->or(eq(length($string), 0), and(ge(length($string), 1), le(length($string), 10))))? ; + (Sub: @subtest)? ; + (SubIgnore: *)? ; + }` + g, err := relapse.Parse(s) + if err != nil { + b.Fatal(err) + } + p := parser.NewReflectParser().Init(reflect.ValueOf(tSuccess)) + bench(b, g, []testsuite.ResetParser{p}, true) +} + +// 7585 ns/op +// func BenchmarkStructComplexFailure(b *testing.B) { + +// validate := New() + +// tFail := &TestString{ +// Required: "", +// Len: "", +// Min: "", +// Max: "12345678901", +// MinMax: "", +// Lt: "0123456789", +// Lte: "01234567890", +// Gt: "1", +// Gte: "1", +// OmitEmpty: "12345678901", +// Sub: &SubTest{ +// Test: "", +// }, +// Anonymous: struct { +// A string `validate:"required"` +// }{ +// A: "", +// }, +// Iface: &Impl{ +// F: "12", +// }, +// } + +// b.ResetTimer() +// for n := 0; n < b.N; n++ { +// validate.Struct(tFail) +// } +// } + +// 414 ns/op +func BenchmarkGoPlaygroundStructComplexFailure(b *testing.B) { + tFail := &TestString{ + Required: "", + Len: "", + Min: "", + Max: "12345678901", + MinMax: "", + Lt: "0123456789", + Lte: "01234567890", + Gt: "1", + Gte: "1", + OmitEmpty: "12345678901", + Sub: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "", + }, + Iface: &Impl{ + F: "12", + }, + } + s := `#main = { + Required->gt(length($string), 0) ; + (Len->eq(length($string), 10))? ; + (Min->ge(length($string), 1))? ; + (Max->le(length($string), 10))? ; + (MinMax->and(ge(length($string), 1), le(length($string), 10)))? ; + (Lt->lt(length($string), 10))? ; + (Lte->le(length($string), 10))? ; + (Gt->gt(length($string), 10))? ; + (Gte->ge(length($string), 10))? ; + (OmitEmpty->or(eq(length($string), 0), and(ge(length($string), 1), le(length($string), 10))))? ; + (Sub: @subtest)? ; + (SubIgnore: *)? ; + }` + g, err := relapse.Parse(s) + if err != nil { + b.Fatal(err) + } + p := parser.NewReflectParser().Init(reflect.ValueOf(tFail)) + bench(b, g, []testsuite.ResetParser{p}, true) +} From 26bc74bdc62e2d9b6b35e39271bf6337873816b4 Mon Sep 17 00:00:00 2001 From: Walter Schulze Date: Tue, 15 Aug 2017 19:48:38 +0200 Subject: [PATCH 2/3] fix for reflect.Int and validate and add benchmarks --- parser/reflect/reflect.go | 2 +- relapse/mem/benchreflect_test.go | 201 ++++++++++++++++++++++++++----- 2 files changed, 169 insertions(+), 34 deletions(-) diff --git a/parser/reflect/reflect.go b/parser/reflect/reflect.go index 9e68dbd..0f9f7f2 100644 --- a/parser/reflect/reflect.go +++ b/parser/reflect/reflect.go @@ -138,7 +138,7 @@ func (s *reflectParser) Int() (int64, error) { if s.isLeaf { value := s.getValue() switch value.Kind() { - case reflect.Int64, reflect.Int32: + case reflect.Int64, reflect.Int32, reflect.Int: return value.Int(), nil } } diff --git a/relapse/mem/benchreflect_test.go b/relapse/mem/benchreflect_test.go index 38fe4f8..79cad3b 100644 --- a/relapse/mem/benchreflect_test.go +++ b/relapse/mem/benchreflect_test.go @@ -36,11 +36,22 @@ func BenchmarkGoPlaygroundStructLevelValidationSuccess(b *testing.B) { String: "good value", } s := ".String:*" + p := parser.NewReflectParser().Init(reflect.ValueOf(tst)) g, err := relapse.Parse(s) if err != nil { b.Fatal(err) } - p := parser.NewReflectParser().Init(reflect.ValueOf(tst)) + m, err := relapse.Prepare(g) + if err != nil { + b.Fatal(err) + } + valid, err := relapse.Validate(m, p) + if err != nil { + b.Fatal(err) + } + if !valid { + b.Fatalf("expected valid") + } bench(b, g, []testsuite.ResetParser{p}, true) } @@ -73,11 +84,22 @@ func BenchmarkGoPlaygroundStructSimpleSuccess(b *testing.B) { (StringValue->and(ge(length($string), 5), le(length($string), 10)))? ; (IntValue->and(ge($int, 5), le($int, 10)))? }` + p := parser.NewReflectParser().Init(reflect.ValueOf(validFoo)) g, err := relapse.Parse(s) if err != nil { b.Fatal(err) } - p := parser.NewReflectParser().Init(reflect.ValueOf(validFoo)) + m, err := relapse.Prepare(g) + if err != nil { + b.Fatal(err) + } + valid, err := relapse.Validate(m, p) + if err != nil { + b.Fatal(err) + } + if !valid { + b.Fatalf("expected valid") + } bench(b, g, []testsuite.ResetParser{p}, true) } @@ -95,32 +117,12 @@ type TestString struct { OmitEmpty string `validate:"omitempty,min=1,max=10"` Sub *SubTest SubIgnore *SubTest `validate:"-"` - Anonymous struct { - A string `validate:"required"` - } - Iface I } type SubTest struct { Test string `validate:"required"` } -type TestInterface struct { - Iface I -} - -type I interface { - Foo() string -} - -type Impl struct { - F string `validate:"len=3"` -} - -func (i *Impl) Foo() string { - return i.F -} - // 1504 ns/op // func BenchmarkStructComplexSuccess(b *testing.B) { @@ -180,6 +182,7 @@ func BenchmarkGoPlaygroundStructComplexSuccess(b *testing.B) { }, } s := `#main = { + BlankTag->eq(length($string), 0) ; Required->gt(length($string), 0) ; (Len->eq(length($string), 10))? ; (Min->ge(length($string), 1))? ; @@ -192,12 +195,26 @@ func BenchmarkGoPlaygroundStructComplexSuccess(b *testing.B) { (OmitEmpty->or(eq(length($string), 0), and(ge(length($string), 1), le(length($string), 10))))? ; (Sub: @subtest)? ; (SubIgnore: *)? ; - }` + } + + #subtest = Test->gt(length($string), 0) + ` + p := parser.NewReflectParser().Init(reflect.ValueOf(tSuccess)) g, err := relapse.Parse(s) if err != nil { b.Fatal(err) } - p := parser.NewReflectParser().Init(reflect.ValueOf(tSuccess)) + m, err := relapse.Prepare(g) + if err != nil { + b.Fatal(err) + } + valid, err := relapse.Validate(m, p) + if err != nil { + b.Fatal(err) + } + if !valid { + b.Fatalf("expected valid") + } bench(b, g, []testsuite.ResetParser{p}, true) } @@ -252,16 +269,9 @@ func BenchmarkGoPlaygroundStructComplexFailure(b *testing.B) { Sub: &SubTest{ Test: "", }, - Anonymous: struct { - A string `validate:"required"` - }{ - A: "", - }, - Iface: &Impl{ - F: "12", - }, } s := `#main = { + BlankTag->eq(length($string), 0) ; Required->gt(length($string), 0) ; (Len->eq(length($string), 10))? ; (Min->ge(length($string), 1))? ; @@ -274,11 +284,136 @@ func BenchmarkGoPlaygroundStructComplexFailure(b *testing.B) { (OmitEmpty->or(eq(length($string), 0), and(ge(length($string), 1), le(length($string), 10))))? ; (Sub: @subtest)? ; (SubIgnore: *)? ; - }` + } + + #subtest = Test->gt(length($string), 0) + ` + p := parser.NewReflectParser().Init(reflect.ValueOf(tFail)) g, err := relapse.Parse(s) if err != nil { b.Fatal(err) } + m, err := relapse.Prepare(g) + if err != nil { + b.Fatal(err) + } + valid, err := relapse.Validate(m, p) + if err != nil { + b.Fatal(err) + } + if valid { + b.Fatalf("expected invalid") + } + bench(b, g, []testsuite.ResetParser{p}, true) +} + +// 132 ns/op +func BenchmarkGoPlaygroundStructComplexSuccess2(b *testing.B) { + tSuccess := &TestString{ + Required: "Required", + Len: "length==10", + Min: "min=1", + Max: "1234567890", + MinMax: "12345", + Lt: "012345678", + Lte: "0123456789", + Gt: "01234567890", + Gte: "0123456789", + OmitEmpty: "", + Sub: &SubTest{ + Test: "1", + }, + SubIgnore: &SubTest{ + Test: "", + }, + } + s := `#main = [ + BlankTag->eq(length($string), 0) , + Required->gt(length($string), 0) , + Len->eq(length($string), 10) , + Min->ge(length($string), 1) , + Max->le(length($string), 10) , + MinMax->and(ge(length($string), 1), le(length($string), 10)) , + Lt->lt(length($string), 10) , + Lte->le(length($string), 10) , + Gt->gt(length($string), 10) , + Gte->ge(length($string), 10) , + OmitEmpty->or(eq(length($string), 0), and(ge(length($string), 1), le(length($string), 10))) , + Sub: @subtest , + SubIgnore: * + ] + + #subtest = Test->gt(length($string), 0) + ` + p := parser.NewReflectParser().Init(reflect.ValueOf(tSuccess)) + g, err := relapse.Parse(s) + if err != nil { + b.Fatal(err) + } + m, err := relapse.Prepare(g) + if err != nil { + b.Fatal(err) + } + valid, err := relapse.Validate(m, p) + if err != nil { + b.Fatal(err) + } + if !valid { + b.Fatalf("expected valid") + } + bench(b, g, []testsuite.ResetParser{p}, true) +} + +// 129 ns/op +func BenchmarkGoPlaygroundStructComplexFailure2(b *testing.B) { + tFail := &TestString{ + Required: "", + Len: "", + Min: "", + Max: "12345678901", + MinMax: "", + Lt: "0123456789", + Lte: "01234567890", + Gt: "1", + Gte: "1", + OmitEmpty: "12345678901", + Sub: &SubTest{ + Test: "", + }, + } + s := `#main = [ + BlankTag->eq(length($string), 0) , + Required->gt(length($string), 0) , + Len->eq(length($string), 10) , + Min->ge(length($string), 1) , + Max->le(length($string), 10) , + MinMax->and(ge(length($string), 1), le(length($string), 10)) , + Lt->lt(length($string), 10) , + Lte->le(length($string), 10) , + Gt->gt(length($string), 10) , + Gte->ge(length($string), 10) , + OmitEmpty->or(eq(length($string), 0), and(ge(length($string), 1), le(length($string), 10))) , + Sub: @subtest , + SubIgnore: * + ] + + #subtest = Test->gt(length($string), 0) + ` p := parser.NewReflectParser().Init(reflect.ValueOf(tFail)) + g, err := relapse.Parse(s) + if err != nil { + b.Fatal(err) + } + m, err := relapse.Prepare(g) + if err != nil { + b.Fatal(err) + } + valid, err := relapse.Validate(m, p) + if err != nil { + b.Fatal(err) + } + if valid { + b.Fatalf("expected invalid") + } bench(b, g, []testsuite.ResetParser{p}, true) } From e256d4f0d03deb6a2d63dc3fcb3512afc006cf2c Mon Sep 17 00:00:00 2001 From: Walter Schulze Date: Tue, 15 Aug 2017 19:58:08 +0200 Subject: [PATCH 3/3] add license --- relapse/mem/benchreflect_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/relapse/mem/benchreflect_test.go b/relapse/mem/benchreflect_test.go index 79cad3b..10d251a 100644 --- a/relapse/mem/benchreflect_test.go +++ b/relapse/mem/benchreflect_test.go @@ -1,3 +1,17 @@ +// Copyright 2016 Walter Schulze +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package mem_test import (