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
fsharp-giraffe #1
Conversation
This is awesome, thank you!
Yes, I can see what a difference this makes. I just want to take the best performance from each language, so long as it doesn't mess with the programming model (as is, we don't have the write terrible code to get excellent performance). So I'd suggest we remove the suave benchmarks entirely.
Great, this aligns nicely with the goals.
This is an interesting optimization, similar to the partial async options. If I think of the programming model I want to support in Dark (which cause the "rules" of the benchmark), I think it would be better for a benchmark to only have a single eval function, not two (also if you look at the logic of the real dark eval function, it is much more complicated and so duplicating it would be risky). I'd suggest breaking this into three benchmarks:
By the optimized version, I mean having two stdlib function types, sync and async, like you have here. I'd be very interested to see if they have performance benefits, so having them as a separate benchmark vs the "plain" async would be super interesting.
Appreciate it! Though, I think best to remove them all the same.
Thanks. As a complete newbie in F#, this advice is appreciated!
I'll take a look at fixing these, thanks. Might add docker or CI now there are contributors (also, matches the deployment model). |
Oh, one more thing to add. The important thing on the benchmark is not how high the fizzbuzz results go, but about making the fizzboom results go up. Right now they all suck - presumably because I'm not coding them right to allow other requests to be processed while the |
I might be misunderstanding what you mean, but I believe the giraffe-async version already works that way. The current benchmark runs 10 connections with a minimum response time of one second making the ideal result 10 requests/sec, right? The giraffe-async is at 8.99 with an average time of 1.03s. Dropping the benchmark duration down to two seconds still results in more than 2 requests/sec meaning each request isn't waiting for the previous to finish the httpbin request. Regarding the simple/optimized async versions, there is a significant performance penalty wrapping all the |
Yes, you're correct here. I've realized there's a problem in how I'm benchmarking. I'm working on improving that.
I'm setting up the rules of the game such that maintenance is paramount. I can see arguments in the other direction but having only 1 eval definition is the rule I'm working with :) If it can be made work with a single eval and without greatly complicating the code, I think that's a fair implementation though. I saw similar slowdowns with the suave version, and the fsharp-suave-partial-async was an attempt to workaround that which was surprisingly effective. |
I hand merged this in and I can address the issues I raised. Thanks again for the great PR! |
@rfvgyhn I have some questions about the code here. After benchmarking it, I realized that it didn't have the right semantics. Fizzboom should always take 6s minimum, but as you can see in the results, this has a much lower average runtime. This is because it's running all the Fizzboom's requests in parallel, see:
The intended way is for them to block (and instead process other computation while blocking), see the OCaml version:
Do you know how I'd do that with Tasks? |
I completely overlooked that. I'm not sure how it works in OCaml, but one of the main differences between |
I'm not sure what hot and cold mean here. Do you mean that the thread running a task does not move to other work during IO? |
nvm, found a definition, thanks |
@pbiggar Just wanted to mention you might want to download RC1 of .net 5 (which is scheduled for full release in November) and run your benchmarks against that. Big performance gains for giraffe. https://nitter.net/ben_a_adams/status/1309139564099964936 |
Thanks! Also, fyi, I figured out how to do in-order execution with tasks: db50aa1 |
Edit: pasted wrong the first time
Before:
After:
|
If you're considering F#, I'd highly recommend using Giraffe over Suave since performance is a concern. Giraffe runs on top of asp.net core/kestrel so it inherits all of its great performance.
Changes:
tasks
instead ofasync
so no conversions have to be done. F# had an async programming model before C# did. C# got async/await later on and has a different implementation viaTask
s.Expr
requires any async calls. If not (fizzbuzz), it runs synchronous method calls and avoids the overhead ofTask
s. This was just a quick hack to get it working. There's a lot of code duplication and poor names.I'm by no means an F# expert (lot's of .net experience though) so I can't really critique the F# itself. I just wanted to make sure you had a giraffe benchmark so you can make a more informed decision. If you do decide on F#, you might also consider using Saturn which is an opinionated web framework that runs on top of Giraffe.
I wasn't able to run the ocaml projects without modification. I didn't include those changes in the branch though. Dune wouldn't build on my machine (arch linux) so I had to use v2.7.0. I also needed to rename
ocaml-sync.opam
toocaml-httpaf.opam
. Theexe
symlink was missing in theocaml-httpaf-lwt
project as well.Here are the results of each on my machine.