4 ms·
Nice. My biggest problem with Rust is the poor performance and lack of complete & sane libraries, especially for HTTP. I was running into serious issues with t
by StreamBright 10y ago
Nice. My biggest problem with Rust is the poor performance and lack of complete & sane libraries, especially for HTTP.
I was running into serious issues with the following code:
https://gist.github.com/l1x/5678c0fdfc2c1a6034b8bcc3800de93c https://gist.github.com/l1x/5678c0fdfc2c1a6034b8bcc3800de93c
Its performance is saturated around 60K req/s on a 32 core box with 10G networking while wrk was more than happy to do 3M req/s. I was trying to dig further into why it is slow and it seems that hyper is the culprit. However, I was not able to come by this issue and just used wrk2 for testing. Let me know if anybody has insight into how to go beyond this perfroamance or what are the issues with my code.
- erickt 10y agoHello! It's been a bit since I dug into hyper, but that's surprisingly low. Just to check, did you compile this with optimizations enabled, like `cargo build --release`?
- StreamBright 10y agoYes I did compile like that, even had optimization on. [package] name = "hammer" version = "0.1.0" authors = ["me"] [dependencies] hyper = "0.10" [profile.release] opt-level = 3 debug = false rpath = false lto = false debug-assertions = false codegen-units = 1 panic = 'unwind'
- kaoD 10y agoI'm not sure about performance vs wrk (don't kwow wrk), but just to give an idea: I noticed you're not using Tokio Hyper. Try Hyper's master branch, which fully embraces Tokio's async. EDIT: From wrk's README: > wrk is a modern HTTP benchmarking tool capable of generating significant load when run on a single multi-core CPU. It combines a multithreaded design with scalable event notification systems such as epoll and kqueue. Your example is multithreaded but IIRC release Hyper isn't using event notification async, which Tokio Hyper does. It might help.
- StreamBright 10y agoThanks, I will look into this.
- valarauca1 10y ago- Spawning a new thread for every request isn't good, you can reuse threads this causes a non-trivial Kernel load - The standard channel library isn't bad but it's a far cry from great. You end up allocating every SEND. Which has its trade offs. - You are doing a print in every thread which requires acquiring a global lock, and at least 2 system calls, and a global allocation. - You aren't keeping your client/connection alive. So every new thread has to do a new 3 way handshake every request. 5 way if your is HTTPS.
- StreamBright 10y agoThank you, it would be useful to show how to this properly. Answers: - we are not spawning a thread for each request The following means we are creating N threads and execute a loop in every one of them, unless I misunderstand Rust entirely. for id in 0..NTHREADS { let thread_tx = tx.clone(); thread::spawn(move || { } .... - I am not sure what coould I do about the standard channel library - Doing a print at the very end once, the impact on performance is negligible - keep alive used to be broken with hyper and I am not only looking for keepalive but pipelinening as well. Let me know if you are aware of a HTTP client in Rust that can do both
- boargod 10y agoI could be doing something wrong, but I tried your code using hyper master here and got similar results: https://gist.github.com/anonymous/d5fa7a62ae1d3927878a9762287b1781 https://gist.github.com/anonymous/d5fa7a62ae1d3927878a976228...
- kaoD 10y agoAgain I'm talking out of my ass here (very new to Rust's async) but AFAICT running core.run(work).unwrap() inside the loop is the same as just running it synchronously (unwrap is waiting for work to end, i.e. there's only a single concurrent connection ever per thread.) It'd probably be better to continously run a core per thread and .spawn many tasks for it so that there are several concurrent connections per thread. Or maybe just .run a future which is the .join of multiple .get futures?