-
Notifications
You must be signed in to change notification settings - Fork 32
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
Slow query rendering #158
Comments
Not that I know anything about this topic, but aren't I was thinking that since at least for me most of the queries are parameterized, would it make sense to somehow pre-allocate the query itself and when run, only supply the parameters, for example constructing the final query with functionality from |
That's another idea, use |
Looks like query rendering perf is definitely not an easy problem to sort out: I don't know if I'm able to help - I'm fairly novice at Haskell & haven't even been able to put comlex queries together myself yet - but what kind of benchmark suite would help the best here? I'm assuming profiling time is what can provide the best insights here. Queries affect endpoint latencies the most, so I think this is worth exploring. |
Hasql has some benchmarks that might be worth looking at. |
@echatav I put in some more effort trying to benchmark real queries against DB. I feel I'm almost there, but there is some issue with connection pooling. Any chance you could take a look? I'm missing something here that I can't see. Once it's fixed it could be extended very easily to accommodate various queries and manipulations. The repo is here: This line throws when trying to run the queries. I feel it may have something to do with me writing the DeepSeq instance manually in the same file on line 40. |
@echatav actually, never mind, I think it worked but just tripped on the random data generation violating constraints in the DB. I'll try to get it to a mergeable condition tomorrow. |
Once we agree that the benchmarks in place are reflective enough to work against, I suggest we start off experimenting by inlining most/all I'm talking about using the
For reference for us to establish a solid baseline on the perf optimizations, here's what I was taught today by Alexis King about what By @lexi-lambda:Inlining is one of the essential compiler optimizations in virtually all languages. The obvious immediate benefit is that it avoids a function call, but that’s not usually very important on modern hardware. The real reason inlining is useful is that it exposes further optimizations, which is especially important in Haskell because idiomatic Haskell code uses lots of abstractions that we’d like the compiler to eliminate. As an example of why inlining do { x <- pure 12; pure (x * 2) } :: Either () Int we know this is equivalent to
Note that most of these optimization steps were only possible because of inlining, but the inlining itself doesn’t get us the wins: it’s beta reduction, case-of-known-constructor, and constant propagation that helps us get the code we want. Inlining just reveals the optimizations to the rest of the optimization pipeline. In the case of small functions like For that reason, compilers tend to be conservative about inlining. They use heuristics to determine when it’s safe to inline a function, and those heuristics are usually pretty good for most code, but for high-performance code, it’s good to write explicit A few more details: inlining is a pretty heavy hammer due to the code size/speed tradeoff mentioned above, so GHC has various other mechanisms to eliminate overhead that explode code size much less than full-blown inlining. Specialization is a weaker approach that generates a single copy of a function per type it’s called at, so for example if you have StrictnessOn the question of strictness: inlining doesn’t affect strictness directly. But as the above example demonstrates, inlining can expose other optimizations, and it’s possible that those further optimizations may reveal more strictness opportunities to the strictness analyzer. So yes, inlining can improve strictness analysis in the same way it improves the utility of other optimizations. |
Something I didn’t mention in the Slack discussion but is nevertheless extremely important when discussing inlining and specialization: read the generated Core. You can ask GHC to dump the optimized Core for a module by compiling with Optimized Core lets you see exactly what the compiler is or is not doing, and it can help you see whether or not the effects of forcing inlining on something is a win. If you add an Reading Core is a learned skill, as it can be overwhelming to look at the first time you see it. The syntax looks like Haskell, but after all of GHC’s optimizations, your program may be completely unrecognizable. However, with some practice, you can learn to see patterns in the generated Core that can tell you very important things! |
I've only been trying to read the call stack from I've only learned to mark Cost Centers manually to flag the interesting parts more clearly. I assume it may compromise Directly diving into the produced source is an excellent tip, thank you! Though more verbose, |
You have to be very careful when using cost center profiling because cost center annotations can prevent compiler optimizations, which rather defeats the purpose. Reading the dumped core can help with that, too: it can show you if certain optimizations aren’t firing due to the As a rule of thumb, putting an If doing that is too challenging for what you’re trying to profile, you might want to look into eventlog-based profiling instead of cost center profiling; see Performance profiling with ghc-events-analyze for the details. |
Yes, that was precisely my point, I wouldn't think it's possible to When profiling, Haskell seems to assign all top level function definitions automatically as Cost Centers, which makes sense. I was actually trying to get productive with |
GHC doesn’t add any cost centers automatically by default, but you can tell it to do so by passing the However, |
Oh, so it was |
I dance around my laptop and chant to improve performance. It's more art than science. |
Large queries can be slow to render (the first time). This might benefit from more strictness. I don't really know how to do Haskell performance very well. If someone would like to experiment with making all the
ByteString
newtypes strict in their arguments with!
and aggressively evaluating inRenderSQL
instances and benchmarking, it would make a great, simple contribution!The text was updated successfully, but these errors were encountered: