Skip to content

Commit

Permalink
added bench
Browse files Browse the repository at this point in the history
  • Loading branch information
guamoko995 committed Apr 27, 2024
1 parent 3ff5185 commit 91a97e4
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 4 deletions.
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ BuildСalcFunc builds the СalcFunc function. CalcFunc effectively calculates th
- ```*``` multiplication
- ```/``` division

TO DO add more operators and functions
TODO add more operators and functions

## Usage

Expand Down Expand Up @@ -43,5 +43,20 @@ func main() {
}
```

## Benchmarks
TO DO add bench results
## Benchmark
[benchmark source code](https://github.com/guamoko995/calcbuilder/blob/master/calcbuilder_bench_test.go)

```
goos: linux
goarch: amd64
pkg: github.com/guamoko995/calcbuilder
cpu: AMD Ryzen 5 5600H with Radeon Graphics
Benchmark/calc_builder-12 61060794 19.77 ns/op 0 B/op 0 allocs/op
Benchmark/compiled-12 216661424 5.583 ns/op 0 B/op 0 allocs/op
Benchmark/compiled_in_var-12 214518717 5.562 ns/op 0 B/op 0 allocs/op
Benchmark/calc_runtime-12 12651817 94.51 ns/op 56 B/op 7 allocs/op
PASS
ok github.com/guamoko995/calcbuilder 7.015s
```

TODO Understand why there are allocations in runtime calculations of the benchmark.
178 changes: 178 additions & 0 deletions calcbuilder_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package calcbuilder

import (
"strconv"
"strings"
"testing"

"github.com/pkg/errors"
)

var (
vars []float64
getVars func(i int) float64
)

func Benchmark(b *testing.B) {
// x0 = 70, x1 = 30
vars = []float64{70, 30}
getVars = func(i int) float64 { return vars[i] }

b.Run("calc builder", func(b *testing.B) {
// (x0 * x1) / (x0 + x1)
calc, err := BuildСalcFunc("/ * x0 x1 + x0 x1", getVars)
if err != nil {
panic(err)
}

if calc() != 21 {
panic("wrong answer")
}

b.ResetTimer()
for i := 1; i <= b.N; i++ {
_ = calc() // (70 * 30) / (70 + 30)
}
b.StopTimer()
})

b.Run("compiled", func(b *testing.B) {
if compiled() != 21 {
panic("wrong answer")
}

b.ResetTimer()
for i := 1; i <= b.N; i++ {
_ = compiled() // (70 * 30) / (70 + 30)
}
b.StopTimer()
})

b.Run("compiled in var", func(b *testing.B) {
// (x0 * x1) / (x0 + x1)
compilInVar := func() float64 {
return getVars(0) * getVars(1) / (getVars(0) + getVars(1))
}

if compilInVar() != 21 {
panic("wrong answer")
}

b.ResetTimer()
for i := 1; i <= b.N; i++ {
_ = compilInVar() // (70 * 30) / (70 + 30)
}
b.StopTimer()
})

b.Run("calc runtime", func(b *testing.B) {
// (x0 * x1) / (x0 + x1)
c, err := newCalcRuntime("x0 x1 * x0 x1 + /", getVars)
if err != nil {
panic(err)
}

if c.calc() != 21 {
panic("wrong answer")
}

b.ResetTimer()
for i := 1; i <= b.N; i++ {
_ = c.calc() // (70 * 30) / (70 + 30)
}
b.StopTimer()
})
}

// (x0 * x1) / (x0 + x1)
func compiled() float64 {
return getVars(0) * getVars(1) / (getVars(0) + getVars(1))
}

type calcRuntime struct {
stack []any
frml []any
getVar func(int) float64
}

func newCalcRuntime(expression string, getVar func(int) float64) (*calcRuntime, error) {
terms := strings.Split(expression, " ")
c := &calcRuntime{
stack: make([]any, len(terms)),
frml: make([]any, 0, len(terms)),
getVar: getVar,
}
for _, term := range terms {
switch term {
case "+", "-", "*", "/":
c.frml = append(c.frml, term)
default:
if term[0] == 'x' {
v, err := strconv.ParseInt(term[1:], 10, 64)
if err != nil {
return nil, errors.Wrap(err, "failed parse var")
}
c.frml = append(c.frml, int(v))
} else {
v, err := strconv.ParseFloat(term, 64)
if err != nil {
return nil, errors.Wrap(err, "failed parse var")
}
c.frml = append(c.frml, v)
}
}
}
return c, nil
}

func (c *calcRuntime) calc() float64 {
lenStack := 0
for _, terml := range c.frml {
switch s := (terml).(type) {
case float64:
c.stack[lenStack] = s
lenStack++
case int:
v := c.getVar(s)
c.stack[lenStack] = v
lenStack++
case string:
switch s {
case "+", "-", "*", "/":
if lenStack == 0 {
panic("formula line is not valid")
}
arg2 := c.stack[lenStack-1].(float64)
lenStack--

if lenStack == 0 {
panic("formula line is not valid")
}
arg1 := c.stack[lenStack-1].(float64)
lenStack--

switch s {
case "+":
c.stack[lenStack] = arg1 + arg2
case "-":
c.stack[lenStack] = arg1 - arg2
case "*":
c.stack[lenStack] = arg1 * arg2
case "/":
c.stack[lenStack] = arg1 / arg2
default:
panic("formula line is not valid")
}
lenStack++
default:
panic("formula line is not valid")
}
default:
panic("formula line is not valid")
}
}
if lenStack != 1 {
panic("formula line is not valid")
}
return c.stack[0].(float64)
}
2 changes: 1 addition & 1 deletion calcbuilder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestBuildCalcFunc(t *testing.T) {
truTest(t, data)
})

// TO DO add error tests
// TODO add error tests
}

type DataSet struct {
Expand Down

0 comments on commit 91a97e4

Please sign in to comment.