-
Notifications
You must be signed in to change notification settings - Fork 22
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
Benchmarks #22
Comments
Thanks @xsc for putting time on the performance. The performance is really important for this library. For the parser performance, it is easy to support caching. I plan to enhance executor to accept parsed query. Once it is done, it will be up to user on how to cache parsed query. It is even a security feature. Since you can choose to accept only cached query. And the parser will never been exposed to arbitrary query. I have heard this is what Facebook doing. |
I am debating the interface of executor. Two options so far:
I will leave this issue open for more comments and ideas. |
I'm in favor of a new function, and in general I think that separating the parsing (and validation) step from the execution step (with a cache in between) makes a lot of sense. This takes performance optimization pressure off of the parse / validate steps, which are complex, and also reduces the number of corner cases that can leak into the execution phase. If it were me, I'd focus on (in order):
|
I've thought about this a bit more, and I would propose we make validation a mandatory upstream step from execution. My reasoning:
|
Just want to have more discussion around data and flow: Existing flow: Query/Schema String -- Parser --> Parsed AST -- Transformation --> Transformed Map -- Executor --> Result Complete flow: Query/Schema String -- Parser --> Parsed AST -- Transformation --> Transformed Map -- Validator --> Validation Error or Validated Map -- Executor --> Executor Error or Result
|
Adding to that, it might be reasonable to have a separate repository/artifact specifying (as in For example, I've been trying out ANTLR4 for parsing (here), producing an AST conforming to this spec. I'd very much like to reuse an existing validator implementation (or plug in a different executor subsystem, e.g. one based on claro) but for that we need a common AST format that isn't prone to change (bar a GraphQL spec update). (This also adds to @aew's "robustness" and "code cleanliness" points since those things can be tested and tuned independently.) Thus, generally, I'm very much in favour of using more of a pipeline approach ( |
@xsc Parsing, validation and execution phase are separated intentionally, in order to make those parts pluggable. It is still better to keep it in one repository, so it is easy to maintain and release. I am open to separate the library to multiple repositories, once it is stable enough. You can still use some parts of graphql-clj as you wish. I agree it is very important to come up right spec/format for "Transformed Map". @aew did great job in introducing clojure.spec in validation. This helps us understand what exactly needs to be in the spec/format. I hope we can come to certain conclusion once validation is done. For executor, I prefer to reuse the same spec/format as "Transformed Map" if possible. It will be painful to maintain two set of spec/format. (#35) Current executor is not the ideal solution. There are a few things needed here:
@xsc I have a quick peek of claro, looks pretty promising. I had looked into muse before. I prefer something simple instead. There are a lot of things can be improved, I will create issue for some of them. It will be great, we can keep getting comments/ideas around them. |
Having thought a bit more about this, I think that validation is intended to be a mandatory step (albeit one completed outside the inner loop of repeatedly executing a given query against a given schema): https://facebook.github.io/graphql/#sec-Validation
This part of the spec explicitly states that execution should not occur for invalid requests. On top of this, it is more work to also support transformations directly from parse output to execution input rather than consistently arriving at execution via the validation phase, which introduces additional transformations needed for validation (and likely useful for an execution tree visit too). |
The parser performance is about 200 times faster in most recent version 0.2.3 with Java Parser. Here is a result for comparison: Case: :complex-query Found 9 outliers in 60 samples (15.0000 %) Detail performance bench can be found in: https://github.com/xsc/graphql-clj-bench/blob/master/result-0.2.3.txt |
First off, thanks for the work on this – it's exciting stuff! I'm aware that any optimisation efforts are probably still far on the horizon but I took the liberty of creating a small set of benchmarks in this repository, currently only for query parsing.
Unfortunately, as of now, it seems that parsing incurs nearly intolerable overhead. For example, the following query (which I don't feel is an unfairly contrived example) clocks in at around 50ms mean parsing time:
Raw Output for the
:complex-query
testcase (here):Most of this seems to be instaparse, though, as shown by
:complex-query-instaparse-only
:Anyway, I just wanted to bring this to your attention. Things like query caching would surely help in a production context, but parsing overhead seems to me like something that can't hurt to reduce. Of course, if GraphQL is just a hard to parse language, there's nothing to be done.
Update: There was a problem with the benchmarks, causing a higher mean (~120ms) than the actual result – which is a lot better but 50ms might still contribute massively to resolution times.
The text was updated successfully, but these errors were encountered: