/ bench / jsonBench.nim
jsonBench.nim
  1  import "../yaml", commonBench
  2  
  3  from nimlets_yaml import objKind
  4  
  5  import math, strutils, stopwatch, terminal, algorithm, random, json
  6  
  7  proc cmp(left, right: clock): int = cmp(left.nanoseconds(), right.nanoseconds())
  8  
  9  type
 10      ObjectKind = enum
 11          otMap, otSequence
 12  
 13      Level = tuple
 14          kind: ObjectKind
 15          len: int
 16  
 17  proc genString(maxLen: int): string =
 18      let len = random(maxLen)
 19      result = "\""
 20      var i = 0
 21      while i < len - 1:
 22          let c = cast[char](random(127 - 32) + 32)
 23          case c
 24          of '"', '\\':
 25              result.add('\\')
 26              result.add(c)
 27              i += 2
 28          else:
 29              result.add(c)
 30              i += 1
 31      result.add('\"')
 32  
 33  proc genJsonString(size: int, maxStringLen: int): string =
 34      ## Generates a random JSON string.
 35      ## size is in KiB, mayStringLen in characters.
 36  
 37      randomize(size * maxStringLen)
 38      result = "{"
 39  
 40      let targetSize = size * 1024
 41      var
 42          indentation = 2
 43          levels = newSeq[Level]()
 44          curSize = 1
 45          justOpened = true
 46      levels.add((kind: otMap, len: 0))
 47  
 48      while levels.len > 0:
 49          let
 50              objectCloseProbability =
 51                  float(levels[levels.high].len + levels.high) * 0.025
 52              closeObject = random(1.0) <= objectCloseProbability
 53  
 54          if (closeObject and levels.len > 1) or curSize > targetSize:
 55              indentation -= 2
 56              if justOpened:
 57                  justOpened = false
 58              else:
 59                  result.add("\x0A")
 60                  result.add(repeat(' ', indentation))
 61                  curSize += indentation + 1
 62              case levels[levels.high].kind
 63              of otMap:
 64                  result.add('}')
 65              of otSequence:
 66                  result.add(']')
 67              curSize += 1
 68              discard levels.pop()
 69              continue
 70  
 71          levels[levels.high].len += 1
 72  
 73          if justOpened:
 74              justOpened = false
 75              result.add("\x0A")
 76              result.add(repeat(' ', indentation))
 77              curSize += indentation + 1
 78          else:
 79              result.add(",\x0A")
 80              result.add(repeat(' ', indentation))
 81              curSize += indentation + 2
 82  
 83          case levels[levels.high].kind
 84          of otMap:
 85              let key = genString(maxStringLen)
 86              result.add(key)
 87              result.add(": ")
 88              curSize += key.len + 2
 89          of otSequence:
 90              discard
 91  
 92          let
 93              objectValueProbability =
 94                  0.8 / float(levels.len * levels.len)
 95              generateObjectValue = random(1.0) <= objectValueProbability
 96  
 97          if generateObjectValue:
 98              let objectKind = if random(2) == 0: otMap else: otSequence
 99              case objectKind
100              of otMap:
101                  result.add('{')
102              of otSequence:
103                  result.add('[')
104              curSize += 1
105              levels.add((kind: objectKind, len: 0))
106              justOpened = true
107              indentation += 2
108          else:
109              var s: string
110              case random(11)
111              of 0..5:
112                  s = genString(maxStringLen)
113              of 6..7:
114                  s = $random(32000)
115              of 8..9:
116                  s = $(random(424242.4242) - 212121.21)
117              of 10:
118                  case random(3)
119                  of 0:
120                      s = "true"
121                  of 1:
122                      s = "false"
123                  of 2:
124                      s = "null"
125                  else:
126                      discard
127              else:
128                  discard
129  
130              result.add(s)
131              curSize += s.len
132  
133  var
134      cYaml1k, cYaml10k, cYaml100k, cJson1k, cJson10k, cJson100k,
135              cLibYaml1k, cLibYaml10k, cLibYaml100k: int64
136      json1k   = genJsonString(1, 32)
137      json10k  = genJsonString(10, 32)
138      json100k = genJsonString(100, 32)
139      tagLib   = initCoreTagLibrary()
140      parser = newYamlParser(initCoreTagLibrary())
141  
142  block:
143      multibench(cJson1k, 100):
144          let res = parseJson(json1k)
145          assert res.kind == JObject
146  
147  block:
148      multibench(cJson10k, 100):
149          let res = parseJson(json10k)
150          assert res.kind == JObject
151  
152  block:
153      multibench(cJson100k, 100):
154          let res = parseJson(json100k)
155          assert res.kind == JObject
156  
157  block:
158      multibench(cYaml1k, 100):
159          let res = loadToJson(json1k)
160          assert res[0].kind == JObject
161  
162  block:
163      multibench(cYaml10k, 100):
164          let res = loadToJson(json10k)
165          assert res[0].kind == JObject
166  
167  block:
168      multibench(cYaml100k, 100):
169          let res = loadToJson(json100k)
170          assert res[0].kind == JObject
171  
172  block:
173      multibench(cLibYaml1k, 100):
174          let res = nimlets_yaml.load(json1k)
175          assert res[0].objKind == nimlets_yaml.YamlObjKind.Map
176  
177  block:
178      multibench(cLibYaml10k, 100):
179          let res = nimlets_yaml.load(json10k)
180          assert res[0].objKind == nimlets_yaml.YamlObjKind.Map
181  
182  block:
183      multibench(cLibYaml100k, 100):
184          let res = nimlets_yaml.load(json100k)
185          assert res[0].objKind == nimlets_yaml.YamlObjKind.Map
186  
187  proc writeResult(caption: string, num: int64) =
188      styledWriteLine(stdout, resetStyle, caption, fgGreen, $num, resetStyle, "μs")
189  
190  setForegroundColor(fgWhite)
191  
192  writeStyled "Benchmark: Processing JSON input\n"
193  writeStyled "================================\n"
194  writeStyled "1k input\n--------\n"
195  writeResult "NimYAML: ", cYaml1k div 1000
196  writeResult "JSON:    ", cJson1k div 1000
197  writeResult "LibYAML: ", cLibYaml1k div 1000
198  setForegroundColor(fgWhite)
199  writeStyled "10k input\n---------\n"
200  writeResult "NimYAML: ", cYaml10k div 1000
201  writeResult "JSON:    ", cJson10k div 1000
202  writeResult "LibYAML: ", cLibYaml10k div 1000
203  setForegroundColor(fgWhite)
204  writeStyled "100k input\n----------\n"
205  writeResult "NimYAML: ", cYaml100k div 1000
206  writeResult "JSON:    ", cJson100k div 1000
207  writeResult "LibYAML: ", cLibYaml100k div 1000