davidavdav / JSObjectLiteral.jl

Parse javascript-like object literals in Julia into a Julia object
MIT License
11 stars 2 forks source link

feature request and performance issue #2

Closed Plasmatium closed 5 years ago

Plasmatium commented 5 years ago

This is an excellent work!!!

Maybe bugs or missing features

Consider this:

# javascript
headers = {
  'content-type': 'application/json'
}

I got an error here in Julia

# julia
headers = @js {
  "content-type": "application/json"
}

UndefVarError: key not defined

Stacktrace:
 [1] top-level scope at In[115]:1

#####################################
# unless:
headers = @js {
    contentType: "application/json"
}

Dict{String,Any} with 1 entry:
  "contentType" => "application/json"

feature request

Is there any way to make functions like JSON.parse and JSON.stringify that similar in javascript? I had tried this, but got some performance issue:

function jsonParse(str::String)
  expr = Meta.parse("@js " * str)
  eval(expr)
end

jsonParse("{a:2, b:{c:3, d:[4, 'e', '5']}}")

Dict{String,Any} with 2 entries:
  "b" => Dict{String,Any}("c"=>3,"d"=>Any[4, 'e', '5'])
  "a" => 2

But didn't find a way to dump dict generated by @js to json string

performance issue

I had a large json string (3748 length).

first on chrome v8:

# javascript
console.time('asdf');
for (let i=0; i<1e4; i++) {
    JSON.parse(str)
}
console.timeEnd('asdf')
VM512:5 asdf: 327.431884765625ms
// this is about 32.74us mean

Julia with @js

# julia
@benchmark @js {
  # ... large string pasted here directly
}

BenchmarkTools.Trial: 
  memory estimate:  15.30 KiB
  allocs estimate:  164
  --------------
  minimum time:     15.026 μs (0.00% GC)
  median time:      17.517 μs (0.00% GC)
  mean time:        19.585 μs (7.82% GC)
  maximum time:     2.794 ms (98.45% GC)
  --------------
  samples:          10000
  evals/sample:     1

really fast, but I don't know if there could be some cache here, or run the whole relevant parsing code each sample.

here is JSON (JSON2 is slightly slower)

# julia
@benchmark JSON.parse(str2)

BenchmarkTools.Trial: 
  memory estimate:  17.30 KiB
  allocs estimate:  193
  --------------
  minimum time:     18.797 μs (0.00% GC)
  median time:      20.749 μs (0.00% GC)
  mean time:        28.182 μs (22.64% GC)
  maximum time:     52.243 ms (99.92% GC)
  --------------
  samples:          10000
  evals/sample:     1

also faster than chrome v8 and slightly slower than JSObjectLiteral(@js) but JSON.json in julia cost 68.794us, 4x time cost as JSON.stringify in chrome v8(cost 16.3us)

But if I use that wrapper function shown before, it hurt performance a lot.

# julia
function jsonParse(str::String)
  expr = Meta.parse("@js " * str)
  eval(expr)
end

@benchmark jsonParse(str)

BenchmarkTools.Trial: 
  memory estimate:  123.56 KiB
  allocs estimate:  1732
  --------------
  minimum time:     3.644 ms (0.00% GC)
  median time:      4.013 ms (0.00% GC)
  mean time:        4.171 ms (1.37% GC)
  maximum time:     51.914 ms (91.56% GC)
  --------------
  samples:          1198
  evals/sample:     1

I tried to split into two process: process for parsing the code and process for evaluation

# julia
# process parse
@benchmark Meta.parse("@js "*str)

BenchmarkTools.Trial: 
  memory estimate:  19.30 KiB
  allocs estimate:  238
  --------------
  minimum time:     1.944 ms (0.00% GC)
  median time:      2.142 ms (0.00% GC)
  mean time:        2.189 ms (0.12% GC)
  maximum time:     5.120 ms (57.70% GC)
  --------------
  samples:          2280
  evals/sample:     1

##################################

# process eval
expr = Meta.parse("@js "*str)
@benchmark eval(expr)

BenchmarkTools.Trial: 
  memory estimate:  104.27 KiB
  allocs estimate:  1494
  --------------
  minimum time:     1.647 ms (0.00% GC)
  median time:      1.758 ms (0.00% GC)
  mean time:        1.796 ms (0.65% GC)
  maximum time:     4.727 ms (55.45% GC)
  --------------
  samples:          2778
  evals/sample:     1

I'm new to Julia, maybe something awesome I missed for parsing and eval code fast.

At last, this is an excellent work!!! I like this repo

davidavdav commented 5 years ago

Hello, thanks for all the testing.

Using a string as a key should of course be possible, there obviously isn't support right now but that would be probably just one line, I'll work on that.

Regarding the timing: you have to be a bit careful with timing macros, I suppose. Macros are evaluated compile-time, whereas you're interested in runtime performance.

Your string parsing function could be simpler: there is the non-exported JSObjectLiteral.js(expr::Expr) so that you could write:

jsonParse(s::AbstractString) = JSObjectLiteral.js(Meta.parse(s))

But realize that the whole idea is that you use JSObjectLiteral to convert Julia code, parsed by the julia compiler, to javascript-like objects. This way you're effectively making js() a run-time javascript-object parser, using the Julia parser.