main.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. "github.com/d5/tengo/v2"
  6. "github.com/d5/tengo/v2/parser"
  7. )
  8. func main() {
  9. runFib(35)
  10. runFibTC1(35)
  11. runFibTC2(35)
  12. }
  13. func runFib(n int) {
  14. start := time.Now()
  15. nativeResult := fib(n)
  16. nativeTime := time.Since(start)
  17. input := `
  18. fib := func(x) {
  19. if x == 0 {
  20. return 0
  21. } else if x == 1 {
  22. return 1
  23. }
  24. return fib(x-1) + fib(x-2)
  25. }
  26. ` + fmt.Sprintf("out = fib(%d)", n)
  27. parseTime, compileTime, runTime, result, err := runBench([]byte(input))
  28. if err != nil {
  29. panic(err)
  30. }
  31. if nativeResult != int(result.(tengo.Int).Value) {
  32. panic(fmt.Errorf("wrong result: %d != %d", nativeResult,
  33. int(result.(tengo.Int).Value)))
  34. }
  35. fmt.Println("-------------------------------------")
  36. fmt.Printf("fibonacci(%d)\n", n)
  37. fmt.Println("-------------------------------------")
  38. fmt.Printf("Result: %d\n", nativeResult)
  39. fmt.Printf("Go: %s\n", nativeTime)
  40. fmt.Printf("Parser: %s\n", parseTime)
  41. fmt.Printf("Compile: %s\n", compileTime)
  42. fmt.Printf("VM: %s\n", runTime)
  43. }
  44. func runFibTC1(n int) {
  45. start := time.Now()
  46. nativeResult := fibTC1(n, 0)
  47. nativeTime := time.Since(start)
  48. input := `
  49. fib := func(x, s) {
  50. if x == 0 {
  51. return 0 + s
  52. } else if x == 1 {
  53. return 1 + s
  54. }
  55. return fib(x-1, fib(x-2, s))
  56. }
  57. ` + fmt.Sprintf("out = fib(%d, 0)", n)
  58. parseTime, compileTime, runTime, result, err := runBench([]byte(input))
  59. if err != nil {
  60. panic(err)
  61. }
  62. if nativeResult != int(result.(tengo.Int).Value) {
  63. panic(fmt.Errorf("wrong result: %d != %d", nativeResult,
  64. int(result.(tengo.Int).Value)))
  65. }
  66. fmt.Println("-------------------------------------")
  67. fmt.Printf("fibonacci(%d) (tail-call #1)\n", n)
  68. fmt.Println("-------------------------------------")
  69. fmt.Printf("Result: %d\n", nativeResult)
  70. fmt.Printf("Go: %s\n", nativeTime)
  71. fmt.Printf("Parser: %s\n", parseTime)
  72. fmt.Printf("Compile: %s\n", compileTime)
  73. fmt.Printf("VM: %s\n", runTime)
  74. }
  75. func runFibTC2(n int) {
  76. start := time.Now()
  77. nativeResult := fibTC2(n, 0, 1)
  78. nativeTime := time.Since(start)
  79. input := `
  80. fib := func(x, a, b) {
  81. if x == 0 {
  82. return a
  83. } else if x == 1 {
  84. return b
  85. }
  86. return fib(x-1, b, a+b)
  87. }
  88. ` + fmt.Sprintf("out = fib(%d, 0, 1)", n)
  89. parseTime, compileTime, runTime, result, err := runBench([]byte(input))
  90. if err != nil {
  91. panic(err)
  92. }
  93. if nativeResult != int(result.(tengo.Int).Value) {
  94. panic(fmt.Errorf("wrong result: %d != %d", nativeResult,
  95. int(result.(tengo.Int).Value)))
  96. }
  97. fmt.Println("-------------------------------------")
  98. fmt.Printf("fibonacci(%d) (tail-call #2)\n", n)
  99. fmt.Println("-------------------------------------")
  100. fmt.Printf("Result: %d\n", nativeResult)
  101. fmt.Printf("Go: %s\n", nativeTime)
  102. fmt.Printf("Parser: %s\n", parseTime)
  103. fmt.Printf("Compile: %s\n", compileTime)
  104. fmt.Printf("VM: %s\n", runTime)
  105. }
  106. func fib(n int) int {
  107. if n == 0 {
  108. return 0
  109. } else if n == 1 {
  110. return 1
  111. } else {
  112. return fib(n-1) + fib(n-2)
  113. }
  114. }
  115. func fibTC1(n, s int) int {
  116. if n == 0 {
  117. return 0 + s
  118. } else if n == 1 {
  119. return 1 + s
  120. }
  121. return fibTC1(n-1, fibTC1(n-2, s))
  122. }
  123. func fibTC2(n, a, b int) int {
  124. if n == 0 {
  125. return a
  126. } else if n == 1 {
  127. return b
  128. } else {
  129. return fibTC2(n-1, b, a+b)
  130. }
  131. }
  132. func runBench(
  133. input []byte,
  134. ) (
  135. parseTime time.Duration,
  136. compileTime time.Duration,
  137. runTime time.Duration,
  138. result tengo.Object,
  139. err error,
  140. ) {
  141. var astFile *parser.File
  142. parseTime, astFile, err = parse(input)
  143. if err != nil {
  144. return
  145. }
  146. var bytecode *tengo.Bytecode
  147. compileTime, bytecode, err = compileFile(astFile)
  148. if err != nil {
  149. return
  150. }
  151. runTime, result, err = runVM(bytecode)
  152. return
  153. }
  154. func parse(input []byte) (time.Duration, *parser.File, error) {
  155. fileSet := parser.NewFileSet()
  156. inputFile := fileSet.AddFile("bench", -1, len(input))
  157. start := time.Now()
  158. p := parser.NewParser(inputFile, input, nil)
  159. file, err := p.ParseFile()
  160. if err != nil {
  161. return time.Since(start), nil, err
  162. }
  163. return time.Since(start), file, nil
  164. }
  165. func compileFile(file *parser.File) (time.Duration, *tengo.Bytecode, error) {
  166. symTable := tengo.NewSymbolTable()
  167. symTable.Define("out")
  168. start := time.Now()
  169. c := tengo.NewCompiler(file.InputFile, symTable, nil, nil, nil)
  170. if err := c.Compile(file); err != nil {
  171. return time.Since(start), nil, err
  172. }
  173. bytecode := c.Bytecode()
  174. bytecode.RemoveDuplicates()
  175. return time.Since(start), bytecode, nil
  176. }
  177. func runVM(
  178. bytecode *tengo.Bytecode,
  179. ) (time.Duration, tengo.Object, error) {
  180. globals := make([]tengo.Object, tengo.GlobalsSize)
  181. start := time.Now()
  182. v := tengo.NewVM(bytecode, globals, -1)
  183. if err := v.Run(); err != nil {
  184. return time.Since(start), nil, err
  185. }
  186. return time.Since(start), globals[0], nil
  187. }