Last active
June 10, 2021 11:05
-
-
Save planetis-m/9f02a1b1ae864b9d0f0a5af8c018df51 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
# Compile with: nim c --gc:orc --panics:on --fieldChecks:on casevariants | |
# Compare with -d:emiDuplicateKey -d:emiLenient | |
import eminim, std/[parsejson, streams] | |
type | |
Fruit = enum | |
Banana, Apple | |
Bar = object | |
shared: int | |
case kind: Fruit | |
of Banana: | |
bad: float | |
banana: int | |
of Apple: apple: string | |
OnceCtx = object | |
shared, kind, bad, banana, apple: bool | |
when defined(emiNextgen): | |
proc transFromBanana(dst: var Bar, p: var JsonParser; kind: Fruit; o: OnceCtx) {.inline.} = | |
when not defined(emiLenient): | |
if o.bad or o.banana: raiseParseErr(p, "valid object field") | |
# Common fields get copied | |
case dst.kind | |
of Apple: | |
var tmpShared: int | |
if o.shared: tmpShared = move dst.shared | |
dst = (typeof dst)(kind: kind, shared: tmpShared) | |
else: discard | |
proc transFromApple(dst: var Bar, p: var JsonParser; kind: Fruit; o: OnceCtx) {.inline.} = | |
when not defined(emiLenient): | |
if o.apple: raiseParseErr(p, "valid object field") | |
case dst.kind | |
of Banana: | |
var tmpShared: int | |
if o.shared: tmpShared = move dst.shared | |
dst = (typeof dst)(kind: kind, shared: tmpShared) | |
else: discard | |
proc initFromJson(dst: var Bar, p: var JsonParser) = | |
var o: OnceCtx | |
eat(p, tkCurlyLe) | |
while p.tok != tkCurlyRi: | |
if p.tok != tkString: | |
raiseParseErr(p, "string literal as key") | |
case p.a | |
of "shared": | |
discard getTok(p) | |
eat(p, tkColon) | |
if not o.shared: | |
o.shared = true | |
initFromJson(dst.shared, p) | |
else: | |
when defined(emiDuplicateKey): skipJson(p) | |
else: raiseParseErr(p, "unique key") | |
of "kind": | |
discard getTok(p) | |
eat(p, tkColon) | |
var kindTmp: Fruit | |
if not o.kind: | |
o.kind = true | |
initFromJson(kindTmp, p) | |
else: | |
when defined(emiDuplicateKey): skipJson(p) | |
else: raiseParseErr(p, "unique key") | |
if dst.kind != kindTmp: | |
case dst.kind | |
of Banana: | |
transFromBanana(dst, p, kindTmp, o) | |
of Apple: | |
transFromApple(dst, p, kindTmp, o) | |
of "bad": | |
case dst.kind | |
of Apple: | |
transFromApple(dst, p, Banana, o) | |
else: discard | |
discard getTok(p) | |
eat(p, tkColon) | |
if not o.bad: | |
o.bad = true | |
initFromJson(dst.bad, p) | |
else: | |
when defined(emiDuplicateKey): skipJson(p) | |
else: raiseParseErr(p, "unique key") | |
of "banana": | |
case dst.kind | |
of Apple: | |
transFromApple(dst, p, Banana, o) | |
else: discard | |
discard getTok(p) | |
eat(p, tkColon) | |
if not o.banana: | |
o.banana = true | |
initFromJson(dst.banana, p) | |
else: | |
when defined(emiDuplicateKey): skipJson(p) | |
else: raiseParseErr(p, "unique key") | |
of "apple": | |
case dst.kind | |
of Banana: | |
transFromApple(dst, p, Apple, o) | |
else: discard | |
discard getTok(p) | |
eat(p, tkColon) | |
if not o.apple: | |
o.apple = true | |
initFromJson(dst.apple, p) | |
else: | |
when defined(emiDuplicateKey): skipJson(p) | |
else: raiseParseErr(p, "unique key") | |
else: | |
when defined(emiLenient): skipJson(p) | |
else: raiseParseErr(p, "valid object field") | |
if p.tok != tkComma: | |
break | |
discard getTok(p) | |
eat(p, tkCurlyRi) | |
#[ | |
let data = | |
#"""[{"shared":1,"kind":"Apple","apple":"world"},{"bad":1.0,"kind":"Banana","shared":2}]""" | |
# Invalid | |
#"""[{"shared":1,"apple":"world","kind":"Banana"}]""" | |
#"""[{"shared":1,"kind":"Apple","apple":"world","kind":"Banana"}]""" | |
# Bug with -d:emiLenient! | |
"""[{"shared":1,"banana":2,"apple":"world","kind":"Banana"}]""" | |
let s = newStringStream(data) | |
echo s.jsonTo(seq[Bar]) | |
]# | |
import std / [times, stats, strformat,random] | |
const MaxIter = 1_000_000 | |
proc warmup() = | |
# Warmup - make sure cpu is on max perf | |
let start = cpuTime() | |
var a = 123 | |
for i in 0 ..< 300_000_000: | |
a += i * i mod 456 | |
a = a mod 789 | |
let dur = cpuTime() - start | |
echo &"Warmup: {dur:>4.4f} s ", a | |
proc printStats(name: string, stats: RunningStat, dur: float) = | |
echo &"""{name}: | |
Collected {stats.n} samples in {dur:>4.4f} s | |
Average time: {stats.mean * 1000:>4.4f} ms | |
Stddev time: {stats.standardDeviationS * 1000:>4.4f} ms | |
Min time: {stats.min * 1000:>4.4f} ms | |
Max time: {stats.max * 1000:>4.4f} ms""" | |
template bench(name, samples, code: untyped) = | |
var stats: RunningStat | |
let globalStart = cpuTime() | |
for i in 0 ..< samples: | |
let start = cpuTime() | |
code | |
let duration = cpuTime() - start | |
stats.push duration | |
let globalDuration = cpuTime() - globalStart | |
printStats(name, stats, globalDuration) | |
proc main = | |
randomize() | |
warmup() | |
var s = newStringStream(newStringOfCap(50*MaxIter)) | |
var data: seq[Bar] | |
for i in 0..<MaxIter: | |
data.add Bar(kind: if rand(1.0) <= 0.5: Banana else: Apple) | |
s.storeJson(data) | |
s.setPosition(0) | |
var | |
a: seq[Bar] | |
bench("emi", 1): | |
a = s.jsonTo(typeof data) | |
echo a[0..4] | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Results on my machine: