-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3ff5185
commit 91a97e4
Showing
3 changed files
with
197 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters