Skip to content

Commit 177a618

Browse files
authored
Merge pull request #17 from treeform/strspeed
Increase string deserialization speed by using size look ahead.
2 parents af21ff9 + aa74c91 commit 177a618

File tree

2 files changed

+86
-13
lines changed

2 files changed

+86
-13
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,20 @@ Another speed up comes from parsing and readings its own numbers directly from m
2929
### Serialize speed
3030
```
3131
name ............................... min time avg time std dv times
32-
treeform/jsony ..................... 1.531 ms 2.779 ms ±0.091 x100
32+
treeform/jsony ..................... 1.317 ms 1.365 ms ±0.054 x100
3333
status-im/nim-json-serialization ... 2.043 ms 3.448 ms ±0.746 x100
3434
planetis-m/eminim .................. 5.951 ms 9.305 ms ±3.210 x100
35-
disruptek/jason ................... 10.312 ms 13.471 ms ±3.107 x100
36-
nim std/json ...................... 12.551 ms 19.419 ms ±4.039 x100
35+
disruptek/jason .................... 6.858 ms 7.043 ms ±0.125 x100
36+
nim std/json ....................... 8.222 ms 8.510 ms ±0.123 x100
3737
```
3838

3939
### Deserialize speed
4040
```
4141
name ............................... min time avg time std dv times
42-
treeform/jsony ..................... 6.724 ms 12.047 ms ±3.694 x100
42+
treeform/jsony ..................... 4.134 ms 4.196 ms ±0.052 x100
4343
status-im/nim-json-serialization ... 7.119 ms 14.276 ms ±2.033 x100
44-
nim std/json ...................... 24.141 ms 38.741 ms ±5.417 x100
45-
planetis-m/eminim ................. 10.974 ms 18.355 ms ±3.994 x100
44+
planetis-m/eminim .................. 7.761 ms 8.001 ms ±0.277 x100
45+
nim std/json ...................... 14.326 ms 14.473 ms ±0.113 x100
4646
```
4747

4848
Note: If you find a faster nim json parser or serializer let me know!

src/jsony.nim

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,7 @@ proc parseHook*(s: string, i: var int, v: var SomeFloat) =
127127
i += chars
128128
v = f
129129

130-
proc parseHook*(s: string, i: var int, v: var string) =
131-
## Parse string.
132-
eatSpace(s, i)
133-
if i + 3 < s.len and s[i+0] == 'n' and s[i+1] == 'u' and s[i+2] == 'l' and s[i+3] == 'l':
134-
i += 4
135-
return
136-
eatChar(s, i, '"')
130+
proc parseStringSlow(s: string, i: var int, v: var string) =
137131
while i < s.len:
138132
let c = s[i]
139133
case c
@@ -161,6 +155,85 @@ proc parseHook*(s: string, i: var int, v: var string) =
161155
inc i
162156
eatChar(s, i, '"')
163157

158+
proc parseStringFast(s: string, i: var int, v: var string) =
159+
# It appears to be faster to scan the string once, then allocate exact chars,
160+
# and then scan the string again populating it.
161+
var
162+
j = i
163+
ll = 0
164+
while j < s.len:
165+
let c = s[j]
166+
case c
167+
of '"':
168+
break
169+
of '\\':
170+
inc j
171+
let c = s[j]
172+
case c
173+
of 'u':
174+
inc j
175+
let u = parseHexInt(s[j ..< j + 4])
176+
j += 3
177+
ll += Rune(u).toUTF8().len
178+
else:
179+
inc ll
180+
else:
181+
inc ll
182+
inc j
183+
184+
if ll > 0:
185+
v = newString(ll)
186+
var
187+
at = 0
188+
ss = cast[ptr UncheckedArray[char]](v[0].addr)
189+
template add(ss: ptr UncheckedArray[char], c: char) =
190+
ss[at] = c
191+
inc at
192+
while i < s.len:
193+
let c = s[i]
194+
case c
195+
of '"':
196+
break
197+
of '\\':
198+
inc i
199+
let c = s[i]
200+
case c
201+
of '"', '\\', '/': ss.add(c)
202+
of 'b': ss.add '\b'
203+
of 'f': ss.add '\f'
204+
of 'n': ss.add '\n'
205+
of 'r': ss.add '\r'
206+
of 't': ss.add '\t'
207+
of 'u':
208+
inc i
209+
let u = parseHexInt(s[i ..< i + 4])
210+
i += 3
211+
for c in Rune(u).toUTF8():
212+
ss.add(c)
213+
else:
214+
ss.add(c)
215+
else:
216+
ss.add(c)
217+
inc i
218+
219+
eatChar(s, i, '"')
220+
221+
proc parseHook*(s: string, i: var int, v: var string) =
222+
## Parse string.
223+
eatSpace(s, i)
224+
if i + 3 < s.len and s[i+0] == 'n' and s[i+1] == 'u' and s[i+2] == 'l' and s[i+3] == 'l':
225+
i += 4
226+
return
227+
eatChar(s, i, '"')
228+
229+
when nimvm:
230+
parseStringSlow(s, i, v)
231+
else:
232+
when defined(js):
233+
parseStringSlow(s, i, v)
234+
else:
235+
parseStringFast(s, i, v)
236+
164237
proc parseHook*(s: string, i: var int, v: var char) =
165238
var str: string
166239
s.parseHook(i, str)

0 commit comments

Comments
 (0)