-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Parsing time is dominated by makeJSONObject (in the wrapper) #5
Comments
Evidently, long term the solution might be to avoid |
Hi @lemire, you are completely right. I verified that it was the case when I was creating the benchmarks for this project and this is the reason why in the benchmark file I use the This problem can be related to Node.js Napi. According to here, Other than that, I see what you mean regarding bringing the computation to C++, maybe we could even add both parsing options, where one would be converting to a JS object (20x slower) and the other would be to use the C++ output. I will also study the possibility of lazily loading/converting the C++ output to a Javascript object. In spite of the way we will implement it, my goal is to keep as simple and as JS-feel as possible. The JS-feel that I mentioned would be that Javascript friendly way of dealing with objects. I will start working on that as soon as possible! Thank you so much for the help! |
It might be possible to keep your design but just find a way to batch it so that it runs faster. I am aware that this is not trivial. |
We have the same problem in pysimdjson where the benchmarks are dominated by the time taken to convert into high-level Python objects. I've been implementing jq-like syntax to avoid creating unnecessary objects and allowing the caller to filter to a subset of the document. I'd like to extend it to allow manipulation of the object as well, but that will take some time to iron out. It's a simple, ugly state machine, and is not optimized at all, but again even then the time is dominated by object creation. No part of it is Python-specific, so it may be an option for you as well. |
Hi @TkTech, I've been looking at your project, it seems very complete. I used your project structure and README file as the start point for the It is good to know that you are having a similar problem, so we can update each other in case of success. Thanks for contributing in this discussion! |
Hi @lemire, I was working on #7 and decided to create some benchmarks for Benchmarks for
|
filename | JSON file | simdjson file |
---|---|---|
apache_builds.json.json | 0.0007966620520429015 | 0.0001389790584036548 |
canada.json.json | 0.02590397239215687 | 0.004743973159827189 |
citm_catalog.json.json | 0.013122753918181821 | 0.008975157412037041 |
github_events.json.json | 0.0007213715816940607 | 0.00032503186619561327 |
gsoc-2018.json.json | 0.01742347150555556 | 0.0035660336778112456 |
instruments.json.json | 0.0009593169606922395 | 0.00024058727887435222 |
marine-ik.json.json | 0.022673223390804596 | 0.0063362718916666685 |
mesh.json.json | 0.005305818097928655 | 0.0015814252134260596 |
mesh.pretty.json.json | 0.007440451715000001 | 0.002563225901495117 |
numbers.json.json | 0.0011234899327846363 | 0.00028067975514213485 |
random.json.json | 0.008677703538160473 | 0.00350299118331916 |
twitter.json.json | 0.006319421839662448 | 0.003089232071381362 |
twitterescaped.json.json | 0.0028453512062915905 | 0.0009503572300291675 |
update-center.json.json | 0.008144905070945946 | 0.002731871923563218 |
Benchmarks for Napi
using parsing the output object to native JS object:
apache_builds.json#simdjson x 438 ops/sec ±0.66% (89 runs sampled) => 0.0022829753812452806
apache_builds.json#JSON x 1,437 ops/sec ±1.05% (87 runs sampled) => 0.0006958411113316021
canada.json#simdjson x 17.05 ops/sec ±3.31% (40 runs sampled) => 0.058656786950000005
canada.json#JSON x 40.14 ops/sec ±3.47% (54 runs sampled) => 0.024911719324074072
citm_catalog.json#simdjson x 39.76 ops/sec ±1.05% (52 runs sampled) => 0.02515266741025641
citm_catalog.json#JSON x 77.57 ops/sec ±2.17% (66 runs sampled) => 0.012891161212121215
github_events.json#simdjson x 700 ops/sec ±0.52% (91 runs sampled) => 0.0014280848411625908
github_events.json#JSON x 1,598 ops/sec ±1.66% (89 runs sampled) => 0.0006256597082786129
gsoc-2018.json#simdjson x 17.66 ops/sec ±1.53% (47 runs sampled) => 0.056629169744680845
gsoc-2018.json#JSON x 54.59 ops/sec ±2.20% (57 runs sampled) => 0.018319231489766073
instruments.json#simdjson x 244 ops/sec ±1.87% (81 runs sampled) => 0.004102743795414463
instruments.json#JSON x 986 ops/sec ±1.89% (85 runs sampled) => 0.0010144264163895345
marine-ik.json#simdjson x 12.66 ops/sec ±1.46% (36 runs sampled) => 0.07900105983333333
marine-ik.json#JSON x 40.90 ops/sec ±1.83% (54 runs sampled) => 0.02445177696604938
mesh.json#simdjson x 71.61 ops/sec ±0.78% (73 runs sampled) => 0.013963813976027396
mesh.json#JSON x 193 ops/sec ±1.49% (80 runs sampled) => 0.005182644167045457
mesh.pretty.json#simdjson x 62.65 ops/sec ±0.73% (65 runs sampled) => 0.01596163256153847
mesh.pretty.json#JSON x 137 ops/sec ±1.26% (76 runs sampled) => 0.007314450536184208
numbers.json#simdjson x 565 ops/sec ±0.95% (87 runs sampled) => 0.0017705686135335555
numbers.json#JSON x 972 ops/sec ±1.42% (86 runs sampled) => 0.0010288740936569055
random.json#simdjson x 56.92 ops/sec ±1.00% (59 runs sampled) => 0.01756998482062147
random.json#JSON x 114 ops/sec ±2.49% (72 runs sampled) => 0.008809404929563494
twitter.json#simdjson x 66.72 ops/sec ±0.78% (68 runs sampled) => 0.01498742863602941
twitter.json#JSON x 153 ops/sec ±1.84% (77 runs sampled) => 0.006541315509379511
twitterescaped.json#simdjson x 85.64 ops/sec ±0.66% (73 runs sampled) => 0.011677340873972602
twitterescaped.json#JSON x 348 ops/sec ±1.35% (87 runs sampled) => 0.0028712606250252057
update-center.json#simdjson x 58.38 ops/sec ±0.85% (62 runs sampled) => 0.01712954294892473
update-center.json#JSON x 127 ops/sec ±2.38% (73 runs sampled) => 0.007865323219911938
filename | JSON file | simdjson file |
---|---|---|
apache_builds.json.json | 0.0006958411113316021 | 0.0022829753812452806 |
canada.json.json | 0.024911719324074072 | 0.058656786950000005 |
citm_catalog.json.json | 0.012891161212121215 | 0.02515266741025641 |
github_events.json.json | 0.0006256597082786129 | 0.0014280848411625908 |
gsoc-2018.json.json | 0.018319231489766073 | 0.056629169744680845 |
instruments.json.json | 0.0010144264163895345 | 0.004102743795414463 |
marine-ik.json.json | 0.02445177696604938 | 0.07900105983333333 |
mesh.json.json | 0.005182644167045457 | 0.013963813976027396 |
mesh.pretty.json.json | 0.007314450536184208 | 0.01596163256153847 |
numbers.json.json | 0.0010288740936569055 | 0.0017705686135335555 |
random.json.json | 0.008809404929563494 | 0.01756998482062147 |
twitter.json.json | 0.006541315509379511 | 0.01498742863602941 |
twitterescaped.json.json | 0.0028712606250252057 | 0.011677340873972602 |
update-center.json.json | 0.007865323219911938 | 0.01712954294892473 |
Benchmarks for Nan
(Google V8) using only the isValid
method:
apache_builds.json#simdjson x 5,322 ops/sec ±0.58% (92 runs sampled) => 0.00018789851454938786
apache_builds.json#JSON x 1,384 ops/sec ±1.34% (90 runs sampled) => 0.0007225356067734202
canada.json#simdjson x 166 ops/sec ±1.16% (83 runs sampled) => 0.006025841941097727
canada.json#JSON x 43.04 ops/sec ±1.32% (56 runs sampled) => 0.023235254416666663
citm_catalog.json#simdjson x 139 ops/sec ±0.83% (78 runs sampled) => 0.007194693341346155
citm_catalog.json#JSON x 79.76 ops/sec ±2.05% (68 runs sampled) => 0.012537133623529408
github_events.json#simdjson x 4,336 ops/sec ±0.81% (93 runs sampled) => 0.00023065048538408372
github_events.json#JSON x 1,604 ops/sec ±1.10% (87 runs sampled) => 0.0006236343558971143
gsoc-2018.json#simdjson x 168 ops/sec ±0.66% (84 runs sampled) => 0.005965806858465609
gsoc-2018.json#JSON x 55.74 ops/sec ±2.51% (60 runs sampled) => 0.017939857340277778
instruments.json#simdjson x 2,928 ops/sec ±0.73% (90 runs sampled) => 0.000341537251819902
instruments.json#JSON x 1,035 ops/sec ±1.78% (89 runs sampled) => 0.000966344505140085
marine-ik.json#simdjson x 116 ops/sec ±1.63% (73 runs sampled) => 0.008624565794520549
marine-ik.json#JSON x 42.65 ops/sec ±1.50% (56 runs sampled) => 0.02344452008928571
mesh.json#simdjson x 523 ops/sec ±0.91% (89 runs sampled) => 0.0019118788956661319
mesh.json#JSON x 196 ops/sec ±1.26% (82 runs sampled) => 0.005095369881374722
mesh.pretty.json#simdjson x 254 ops/sec ±0.64% (84 runs sampled) => 0.003929379895866039
mesh.pretty.json#JSON x 139 ops/sec ±1.28% (78 runs sampled) => 0.007172208120192307
numbers.json#simdjson x 3,047 ops/sec ±0.36% (95 runs sampled) => 0.0003281855913570448
numbers.json#JSON x 955 ops/sec ±1.50% (89 runs sampled) => 0.0010468748176318064
random.json#simdjson x 479 ops/sec ±0.52% (91 runs sampled) => 0.00208574015032967
random.json#JSON x 112 ops/sec ±2.50% (71 runs sampled) => 0.008939500769282363
twitter.json#simdjson x 415 ops/sec ±1.23% (89 runs sampled) => 0.0024099802471910107
twitter.json#JSON x 151 ops/sec ±2.05% (76 runs sampled) => 0.006631303152412284
twitterescaped.json#simdjson x 808 ops/sec ±0.47% (93 runs sampled) => 0.0012375998396825397
twitterescaped.json#JSON x 335 ops/sec ±1.52% (83 runs sampled) => 0.0029858003431268935
update-center.json#simdjson x 465 ops/sec ±0.34% (91 runs sampled) => 0.0021491741295238094
update-center.json#JSON x 123 ops/sec ±2.50% (77 runs sampled) => 0.008158883020408162
filename | JSON file | simdjson file |
---|---|---|
apache_builds.json.json | 0.0007225356067734202 | 0.00018789851454938786 |
canada.json.json | 0.023235254416666663 | 0.006025841941097727 |
citm_catalog.json.json | 0.012537133623529408 | 0.007194693341346155 |
github_events.json.json | 0.0006236343558971143 | 0.00023065048538408372 |
gsoc-2018.json.json | 0.017939857340277778 | 0.005965806858465609 |
instruments.json.json | 0.000966344505140085 | 0.000341537251819902 |
marine-ik.json.json | 0.02344452008928571 | 0.008624565794520549 |
mesh.json.json | 0.005095369881374722 | 0.0019118788956661319 |
mesh.pretty.json.json | 0.007172208120192307 | 0.003929379895866039 |
numbers.json.json | 0.0010468748176318064 | 0.0003281855913570448 |
random.json.json | 0.008939500769282363 | 0.00208574015032967 |
twitter.json.json | 0.006631303152412284 | 0.0024099802471910107 |
twitterescaped.json.json | 0.0029858003431268935 | 0.0012375998396825397 |
update-center.json.json | 0.008158883020408162 | 0.0021491741295238094 |
Benchmarks for Nan
(Google V8) using parsing the output object to native JS object:
apache_builds.json#simdjson x 679 ops/sec ±1.15% (90 runs sampled) => 0.001472604520007262
apache_builds.json#JSON x 1,396 ops/sec ±1.07% (89 runs sampled) => 0.0007163054532996373
canada.json#simdjson x 20.20 ops/sec ±1.28% (37 runs sampled) => 0.04951047694594596
canada.json#JSON x 42.57 ops/sec ±1.67% (56 runs sampled) => 0.023491198351190474
citm_catalog.json#simdjson x 49.75 ops/sec ±0.53% (64 runs sampled) => 0.020098972307291674
citm_catalog.json#JSON x 80.75 ops/sec ±1.84% (69 runs sampled) => 0.012384519811594209
github_events.json#simdjson x 1,217 ops/sec ±0.79% (93 runs sampled) => 0.0008217606435226479
github_events.json#JSON x 1,681 ops/sec ±0.62% (93 runs sampled) => 0.0005949090546686192
gsoc-2018.json#simdjson x 54.21 ops/sec ±0.53% (69 runs sampled) => 0.018445115888888884
gsoc-2018.json#JSON x 55.54 ops/sec ±2.64% (60 runs sampled) => 0.01800485047083333
instruments.json#simdjson x 291 ops/sec ±0.68% (85 runs sampled) => 0.003433637129411764
instruments.json#JSON x 1,006 ops/sec ±1.83% (86 runs sampled) => 0.0009944475654357068
marine-ik.json#simdjson x 14.06 ops/sec ±2.74% (38 runs sampled) => 0.07112410321052634
marine-ik.json#JSON x 42.03 ops/sec ±1.62% (55 runs sampled) => 0.023794345115151515
mesh.json#simdjson x 75.85 ops/sec ±1.58% (65 runs sampled) => 0.013183479455384615
mesh.json#JSON x 193 ops/sec ±1.13% (81 runs sampled) => 0.005189652390011223
mesh.pretty.json#simdjson x 71.35 ops/sec ±0.41% (73 runs sampled) => 0.014015100469178081
mesh.pretty.json#JSON x 137 ops/sec ±1.17% (77 runs sampled) => 0.007275473579545454
numbers.json#simdjson x 601 ops/sec ±0.29% (93 runs sampled) => 0.001663296916406521
numbers.json#JSON x 931 ops/sec ±1.63% (88 runs sampled) => 0.0010745288268493758
random.json#simdjson x 79.04 ops/sec ±0.40% (67 runs sampled) => 0.012652597855223878
random.json#JSON x 108 ops/sec ±2.19% (70 runs sampled) => 0.009232717743537415
twitter.json#simdjson x 96.65 ops/sec ±0.90% (70 runs sampled) => 0.01034642326904762
twitter.json#JSON x 154 ops/sec ±2.12% (77 runs sampled) => 0.006510784343434344
twitterescaped.json#simdjson x 116 ops/sec ±1.18% (76 runs sampled) => 0.00862335357863409
twitterescaped.json#JSON x 334 ops/sec ±1.65% (83 runs sampled) => 0.002994692009370816
update-center.json#simdjson x 98.50 ops/sec ±0.92% (73 runs sampled) => 0.010152052480821917
update-center.json#JSON x 123 ops/sec ±2.61% (71 runs sampled) => 0.008161439740945672
filename | JSON file | simdjson file |
---|---|---|
apache_builds.json.json | 0.0007163054532996373 | 0.001472604520007262 |
canada.json.json | 0.023491198351190474 | 0.04951047694594596 |
citm_catalog.json.json | 0.012384519811594209 | 0.020098972307291674 |
github_events.json.json | 0.0005949090546686192 | 0.0008217606435226479 |
gsoc-2018.json.json | 0.01800485047083333 | 0.018445115888888884 |
instruments.json.json | 0.0009944475654357068 | 0.003433637129411764 |
marine-ik.json.json | 0.023794345115151515 | 0.07112410321052634 |
mesh.json.json | 0.005189652390011223 | 0.013183479455384615 |
mesh.pretty.json.json | 0.007275473579545454 | 0.014015100469178081 |
numbers.json.json | 0.0010745288268493758 | 0.001663296916406521 |
random.json.json | 0.009232717743537415 | 0.012652597855223878 |
twitter.json.json | 0.006510784343434344 | 0.01034642326904762 |
twitterescaped.json.json | 0.002994692009370816 | 0.00862335357863409 |
update-center.json.json | 0.008161439740945672 | 0.010152052480821917 |
Generating native code in V8
is faster, but it seems that there is an initial overhead, since the isValid
method was faster using Napi
. I will have to think a bit more about that, but I will probably be using Napi
since it is faster in the C++ and use one of the approach that the three of us have discussed. Cheers!
Another option would be not to convert the JSON data, piece by piece, into C++ and then JavaScript values. Just expose the C++ "object" by wrapping its methods. Is that practical? |
That is exactly the option I had in mind, will be working on it once I figure #7 out, cheers! |
Hi @lemire @TkTech, In this way, I can use a proxy for my object, trap the getter/setter and return whatever I want. I started to implement it here https://github.com/luizperes/simdjson_nodejs/blob/improve-performance/simdjson/bindings.cpp#L111 Basically, everything I do is calling "widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
}
} I will parse this object using
if the user calls the function
See that the overhead of trapping an object only occurs once for every property (second access to the same field is very fast). Please let me know what you think! |
Assuming that it works well in benchmarks, this is a very appealing design. |
Hi @lemire, I have implemented the |
@luizperes On my todo. |
I confirm the good results. I think we can close the issue, good work! |
Hi @luizperes. I am currently doing a study on simdjson for my master and we are trying to improve performances of the simdjson library in high-level environments such as Node and Python. I downloaded the sources and saw what you did with lazy_parse. This is a huge advancement but I don't see the use of the Javascript Proxy in the sources. I did a little implementation in JS based on your suggestions on this issue and I would like to go further and try to find a way to return a Proxy object directly in C++ with N-API. This way, we could access values directly from object properties instead of calling the method valueForKeyPath and properties would not get loaded twice. |
Hi @croteaucarine, const monster = (target) => {
return new Proxy(target, handler)
};
const simdjson = {
extract: (obj) => { return obj.__simdjsonvalue__ }
};
const handler = {
get: (obj, prop) => {
if (typeof obj[prop] === 'undefined') {
let sprop = String(prop);
let val = !isNaN(parseInt(sprop)) ? "[" + sprop + "]" : "." + sprop;
obj[prop] = monster({ __simdjsonvalue__: obj.__simdjsonvalue__ + val });
return obj[prop];
} else {
return obj[prop];
}
},
set: (obj, prop, value) => {
obj[prop]['__simdjsonvalue__'] = value;
return value;
}
};
// ---------------- EXAMPLES
let proxy = monster({ __simdjsonvalue__: "", keyForValue: (str) => str + " sss" });
console.log(proxy.j.f.__simdjsonvalue__);
console.log(simdjson.extract(proxy.j.f));
console.log(proxy.keyForValue("www")); That was the best I could get. Once I realized that I woud need a method The next thing I thought to myself was to implement simdjson directly in the Google V8 project (and submit a patch later), meaning that we would replace the existing I hope what I said helps you to get started and please feel more than welcome to work in this project and like I said, do ask any questions you might have, that will be a pleasure to help! Oh, and sorry for the late reply, I was traveling this past week and couln't find the time to give you a proper response. I usually try to get back to my emails within a day. Lastly, let me know if you want me to reopen this issue and feel free to open new ones. Luiz |
@luizperes I suggest you have a look at https://github.com/croteaucarine/simdjson_node_objectwrap |
Hi @luizperes , I am very interesting about the project of simdjson and thank you for building the bindings of node.js. I have also tested some benchmarks which I am interested in. I found the same phenomenon in issue#28, without lazyparse, the parsing speed by simdjson is slower than default json.parse. But it seems that lazyparse is a good method for parsing JSON. I have tried some methods in the "benchmark.js", however I failed. Maybe my understanding is not enough. Could you show me how can I check it? Thank you~ |
At the C++ level, isValid and parse do exactly the same work. This is obvious from the code in this wrapper:
Versus
However, in
simdjson_nodejs
,parse
is at least 20x slower thanisValid
. This indicates that the running time is entirely dependent onmakeJSONObject
.This function is written in a sensible manner but it is nevertheless quite slow. Evidently, this defeats the purpose of the fast parsing.
I would say it is a priority to fix this performance issue. It seems that there would be different valid ways to go about it.
The text was updated successfully, but these errors were encountered: