template_timing_test.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. package fasttemplate
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "net/url"
  7. "strings"
  8. "testing"
  9. "text/template"
  10. )
  11. var (
  12. source = "http://{{uid}}.foo.bar.com/?cb={{cb}}{{width}}&width={{width}}&height={{height}}&timeout={{timeout}}&uid={{uid}}&subid={{subid}}&ref={{ref}}&empty={{empty}}"
  13. result = "http://aaasdf.foo.bar.com/?cb=12341232&width=1232&height=123&timeout=123123&uid=aaasdf&subid=asdfds&ref=http://google.com/aaa/bbb/ccc&empty="
  14. resultEscaped = "http://aaasdf.foo.bar.com/?cb=12341232&width=1232&height=123&timeout=123123&uid=aaasdf&subid=asdfds&ref=http%3A%2F%2Fgoogle.com%2Faaa%2Fbbb%2Fccc&empty="
  15. resultStd = "http://aaasdf.foo.bar.com/?cb=12341232&width=1232&height=123&timeout=123123&uid=aaasdf&subid=asdfds&ref=http://google.com/aaa/bbb/ccc&empty={{empty}}"
  16. resultTextTemplate = "http://aaasdf.foo.bar.com/?cb=12341232&width=1232&height=123&timeout=123123&uid=aaasdf&subid=asdfds&ref=http://google.com/aaa/bbb/ccc&empty=<no value>"
  17. resultBytes = []byte(result)
  18. resultEscapedBytes = []byte(resultEscaped)
  19. resultStdBytes = []byte(resultStd)
  20. resultTextTemplateBytes = []byte(resultTextTemplate)
  21. m = map[string]interface{}{
  22. "cb": []byte("1234"),
  23. "width": []byte("1232"),
  24. "height": []byte("123"),
  25. "timeout": []byte("123123"),
  26. "uid": []byte("aaasdf"),
  27. "subid": []byte("asdfds"),
  28. "ref": []byte("http://google.com/aaa/bbb/ccc"),
  29. }
  30. )
  31. func map2slice(m map[string]interface{}) []string {
  32. var a []string
  33. for k, v := range m {
  34. a = append(a, "{{"+k+"}}", string(v.([]byte)))
  35. }
  36. return a
  37. }
  38. func BenchmarkFmtFprintf(b *testing.B) {
  39. b.RunParallel(func(pb *testing.PB) {
  40. var w bytes.Buffer
  41. for pb.Next() {
  42. fmt.Fprintf(&w,
  43. "http://%[5]s.foo.bar.com/?cb=%[1]s%[2]s&width=%[2]s&height=%[3]s&timeout=%[4]s&uid=%[5]s&subid=%[6]s&ref=%[7]s&empty=",
  44. m["cb"], m["width"], m["height"], m["timeout"], m["uid"], m["subid"], m["ref"])
  45. x := w.Bytes()
  46. if !bytes.Equal(x, resultBytes) {
  47. b.Fatalf("Unexpected result\n%q\nExpected\n%q\n", x, result)
  48. }
  49. w.Reset()
  50. }
  51. })
  52. }
  53. func BenchmarkStringsReplace(b *testing.B) {
  54. mSlice := map2slice(m)
  55. b.ResetTimer()
  56. b.RunParallel(func(pb *testing.PB) {
  57. for pb.Next() {
  58. x := source
  59. for i := 0; i < len(mSlice); i += 2 {
  60. x = strings.Replace(x, mSlice[i], mSlice[i+1], -1)
  61. }
  62. if x != resultStd {
  63. b.Fatalf("Unexpected result\n%q\nExpected\n%q\n", x, resultStd)
  64. }
  65. }
  66. })
  67. }
  68. func BenchmarkStringsReplacer(b *testing.B) {
  69. mSlice := map2slice(m)
  70. b.ResetTimer()
  71. b.RunParallel(func(pb *testing.PB) {
  72. for pb.Next() {
  73. r := strings.NewReplacer(mSlice...)
  74. x := r.Replace(source)
  75. if x != resultStd {
  76. b.Fatalf("Unexpected result\n%q\nExpected\n%q\n", x, resultStd)
  77. }
  78. }
  79. })
  80. }
  81. func BenchmarkTextTemplate(b *testing.B) {
  82. s := strings.Replace(source, "{{", "{{.", -1)
  83. t, err := template.New("test").Parse(s)
  84. if err != nil {
  85. b.Fatalf("Error when parsing template: %s", err)
  86. }
  87. mm := make(map[string]string)
  88. for k, v := range m {
  89. mm[k] = string(v.([]byte))
  90. }
  91. b.ResetTimer()
  92. b.RunParallel(func(pb *testing.PB) {
  93. var w bytes.Buffer
  94. for pb.Next() {
  95. if err := t.Execute(&w, mm); err != nil {
  96. b.Fatalf("error when executing template: %s", err)
  97. }
  98. x := w.Bytes()
  99. if !bytes.Equal(x, resultTextTemplateBytes) {
  100. b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, resultTextTemplateBytes)
  101. }
  102. w.Reset()
  103. }
  104. })
  105. }
  106. func BenchmarkFastTemplateExecuteFunc(b *testing.B) {
  107. t, err := NewTemplate(source, "{{", "}}")
  108. if err != nil {
  109. b.Fatalf("error in template: %s", err)
  110. }
  111. b.ResetTimer()
  112. b.RunParallel(func(pb *testing.PB) {
  113. var w bytes.Buffer
  114. for pb.Next() {
  115. if _, err := t.ExecuteFunc(&w, testTagFunc); err != nil {
  116. b.Fatalf("unexpected error: %s", err)
  117. }
  118. x := w.Bytes()
  119. if !bytes.Equal(x, resultBytes) {
  120. b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, resultBytes)
  121. }
  122. w.Reset()
  123. }
  124. })
  125. }
  126. func BenchmarkFastTemplateExecute(b *testing.B) {
  127. t, err := NewTemplate(source, "{{", "}}")
  128. if err != nil {
  129. b.Fatalf("error in template: %s", err)
  130. }
  131. b.ResetTimer()
  132. b.RunParallel(func(pb *testing.PB) {
  133. var w bytes.Buffer
  134. for pb.Next() {
  135. if _, err := t.Execute(&w, m); err != nil {
  136. b.Fatalf("unexpected error: %s", err)
  137. }
  138. x := w.Bytes()
  139. if !bytes.Equal(x, resultBytes) {
  140. b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, resultBytes)
  141. }
  142. w.Reset()
  143. }
  144. })
  145. }
  146. func BenchmarkFastTemplateExecuteStd(b *testing.B) {
  147. t, err := NewTemplate(source, "{{", "}}")
  148. if err != nil {
  149. b.Fatalf("error in template: %s", err)
  150. }
  151. b.ResetTimer()
  152. b.RunParallel(func(pb *testing.PB) {
  153. var w bytes.Buffer
  154. for pb.Next() {
  155. if _, err := t.ExecuteStd(&w, m); err != nil {
  156. b.Fatalf("unexpected error: %s", err)
  157. }
  158. x := w.Bytes()
  159. if !bytes.Equal(x, resultStdBytes) {
  160. b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, resultStdBytes)
  161. }
  162. w.Reset()
  163. }
  164. })
  165. }
  166. func BenchmarkFastTemplateExecuteFuncString(b *testing.B) {
  167. t, err := NewTemplate(source, "{{", "}}")
  168. if err != nil {
  169. b.Fatalf("error in template: %s", err)
  170. }
  171. b.ResetTimer()
  172. b.RunParallel(func(pb *testing.PB) {
  173. for pb.Next() {
  174. x := t.ExecuteFuncString(testTagFunc)
  175. if x != result {
  176. b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, result)
  177. }
  178. }
  179. })
  180. }
  181. func BenchmarkFastTemplateExecuteString(b *testing.B) {
  182. t, err := NewTemplate(source, "{{", "}}")
  183. if err != nil {
  184. b.Fatalf("error in template: %s", err)
  185. }
  186. b.ResetTimer()
  187. b.RunParallel(func(pb *testing.PB) {
  188. for pb.Next() {
  189. x := t.ExecuteString(m)
  190. if x != result {
  191. b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, result)
  192. }
  193. }
  194. })
  195. }
  196. func BenchmarkFastTemplateExecuteStringStd(b *testing.B) {
  197. t, err := NewTemplate(source, "{{", "}}")
  198. if err != nil {
  199. b.Fatalf("error in template: %s", err)
  200. }
  201. b.ResetTimer()
  202. b.RunParallel(func(pb *testing.PB) {
  203. for pb.Next() {
  204. x := t.ExecuteStringStd(m)
  205. if x != resultStd {
  206. b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, resultStd)
  207. }
  208. }
  209. })
  210. }
  211. func BenchmarkFastTemplateExecuteTagFunc(b *testing.B) {
  212. t, err := NewTemplate(source, "{{", "}}")
  213. if err != nil {
  214. b.Fatalf("error in template: %s", err)
  215. }
  216. mm := make(map[string]interface{})
  217. for k, v := range m {
  218. if k == "ref" {
  219. vv := v.([]byte)
  220. v = TagFunc(func(w io.Writer, tag string) (int, error) { return w.Write([]byte(url.QueryEscape(string(vv)))) })
  221. }
  222. mm[k] = v
  223. }
  224. b.ResetTimer()
  225. b.RunParallel(func(pb *testing.PB) {
  226. var w bytes.Buffer
  227. for pb.Next() {
  228. if _, err := t.Execute(&w, mm); err != nil {
  229. b.Fatalf("unexpected error: %s", err)
  230. }
  231. x := w.Bytes()
  232. if !bytes.Equal(x, resultEscapedBytes) {
  233. b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, resultEscapedBytes)
  234. }
  235. w.Reset()
  236. }
  237. })
  238. }
  239. func BenchmarkNewTemplate(b *testing.B) {
  240. b.RunParallel(func(pb *testing.PB) {
  241. for pb.Next() {
  242. _ = New(source, "{{", "}}")
  243. }
  244. })
  245. }
  246. func BenchmarkTemplateReset(b *testing.B) {
  247. b.RunParallel(func(pb *testing.PB) {
  248. t := New(source, "{{", "}}")
  249. for pb.Next() {
  250. t.Reset(source, "{{", "}}")
  251. }
  252. })
  253. }
  254. func BenchmarkTemplateResetExecuteFunc(b *testing.B) {
  255. b.RunParallel(func(pb *testing.PB) {
  256. t := New(source, "{{", "}}")
  257. var w bytes.Buffer
  258. for pb.Next() {
  259. t.Reset(source, "{{", "}}")
  260. t.ExecuteFunc(&w, testTagFunc)
  261. w.Reset()
  262. }
  263. })
  264. }
  265. func BenchmarkExecuteFunc(b *testing.B) {
  266. b.RunParallel(func(pb *testing.PB) {
  267. var bb bytes.Buffer
  268. for pb.Next() {
  269. ExecuteFunc(source, "{{", "}}", &bb, testTagFunc)
  270. bb.Reset()
  271. }
  272. })
  273. }
  274. func testTagFunc(w io.Writer, tag string) (int, error) {
  275. if t, ok := m[tag]; ok {
  276. return w.Write(t.([]byte))
  277. }
  278. return 0, nil
  279. }