简单 HTTP 性能测试和对比

昨晚无聊用 Kotlin 搭了个 Helloworld 的环境,然后突然就想看下这货的性能跟 Node.js 对比怎么样,顺便又把 Rust 和 C++ 的也测试了。

为了尽可能缩小误差,我本地同时运行了:

  • C++ Crow(multithreaded) 8000 端口
  • C++ Crow 8001 端口
  • Rust Hyper 8002 端口
  • Kotlin Ktor 8003 端口,Run By IDEA
  • Node.js with Cluster 8004 端口
  • Node.js 8005 端口
  • FibJS 8006 端口

预先请求一次,然后进行测试。

C++ Crow(multithreaded) FibJS Node.js(cluster) Kotlin Ktor Rust Hyper C++ Crow Node.js
Requests/sec 99731.78 63044.09 60742.62 60098.01 59796.58 53165.46 26113.64
Transfer/sec 10.27MB 8.84MB 6.60MB 5.33MB 7.47MB 5.48MB 3.29MB

C++ Crow 分别在开启 multithreaded 有无的情况进行了测试;Kotlin 是直接在 IDEA 运行的,我不知道这个会不会意味着什么,但性能可能会有折扣吧;Rust 据说是单线程执行。

要说得出什么结论的话,还真不好说。但比较让我惊喜的是 FibJS 的表现优于 Node.js Cluster 下的表现,并且已经超过 Kotlin Ktor 在 IDEA 运行的表现。不过单核的 Node.js 表现就比较平凡了。

Node.js 是此处唯一的解释型,动态类型的语言。虽然没有对 Python PHP 进行测试,但我估计他们的表现会比单核的 Node.js 差很多。

贴下测试代码和测试结果:

  1. C++ Crow with multithreaded

    #include "crow.h"
    
    int main()
    {
        crow::logger::setLogLevel(crow::LogLevel::ERROR);
        crow::SimpleApp app;
    
        CROW_ROUTE(app, "/")
        ([]() {
            return "Hello, World!\n";
        });
    
        app.port(18080).multithreaded().run();
    }
    

    结果:

    ➜  ~ wrk -t12 -c400 -d60s http://localhost:8000
    Running 1m test @ http://localhost:8000
      12 threads and 400 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency     4.60ms    4.58ms  78.28ms   90.14%
        Req/Sec     8.37k     2.48k   24.23k    75.13%
      5991352 requests in 1.00m, 617.09MB read
    Requests/sec:  99731.78
    Transfer/sec:     10.27MB
    
  2. Kotlin Ktor

    package main
    
    import io.ktor.server.netty.*
    import io.ktor.routing.*
    import io.ktor.application.*
    import io.ktor.http.*
    import io.ktor.response.*
    import io.ktor.server.engine.*
    
    fun main(args: Array<String>) {
        embeddedServer(Netty, 8000) {
            routing {
                get("/") {
                    call.respondText("Hello, world!\n", ContentType.Text.Html)
                }
            }
        }.start(wait = true)
    }
    

    结果:

    ➜  ~ wrk -t12 -c400 -d60s http://localhost:8003
    Running 1m test @ http://localhost:8003
      12 threads and 400 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency     8.29ms   19.79ms 612.33ms   98.13%
        Req/Sec     5.10k     1.09k    9.35k    86.94%
      3608647 requests in 1.00m, 320.06MB read
    Requests/sec:  60098.01
    Transfer/sec:      5.33MB
    
  3. Rust Hyper

    extern crate hyper;
    
    use hyper::header::{ContentLength, ContentType};
    use hyper::server::{Http, Response, const_service, service_fn};
    
    static TEXT: &'static str = "Hello, World!\n";
    
    fn run() -> Result<(), hyper::Error> {
        let addr = ([127, 0, 0, 1], 8002).into();
    
        let hello = const_service(service_fn(|_req|{
            Ok(Response::<hyper::Body>::new()
                .with_header(ContentLength(TEXT.len() as u64))
                .with_header(ContentType::plaintext())
                .with_body(TEXT))
        }));
    
        let server = Http::new().bind(&addr, hello)?;
        server.run()
    }
    
    fn main() {
        run();
    }
    

    结果:

    ➜  ~ wrk -t12 -c400 -d60s http://localhost:8002
    Running 1m test @ http://localhost:8002
      12 threads and 400 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency     6.62ms  561.92us  28.15ms   90.16%
        Req/Sec     5.01k   221.82     8.21k    64.32%
      3591281 requests in 1.00m, 448.66MB read
    Requests/sec:  59796.58
    Transfer/sec:      7.47MB
    
  4. C++ Crow

    #include "crow.h"
    
    int main()
    {
        crow::logger::setLogLevel(crow::LogLevel::ERROR);
        crow::SimpleApp app;
    
        CROW_ROUTE(app, "/")
        ([]() {
            return "Hello, world!\n";
        });
    
        app.port(18080).run();
    }
    
    
    ➜  ~ wrk -t12 -c400 -d60s http://localhost:8001
    Running 1m test @ http://localhost:8001
      12 threads and 400 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency     7.44ms  736.27us  36.50ms   82.15%
        Req/Sec     4.46k   409.19    27.26k    89.00%
      3195199 requests in 1.00m, 329.10MB read
    Requests/sec:  53165.46
    Transfer/sec:      5.48MB
    
  5. fibsjs

    const http = require("http");
    
    var svr = new http.Server(8080, req => {
      req.response.write("hello, world!\n");
    });
    
    svr.run();
    
    ➜  ~ wrk -t12 -c400 -d60s http://localhost:8006
    Running 1m test @ http://localhost:8006
      12 threads and 400 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency     6.33ms    2.53ms  44.03ms   77.67%
        Req/Sec     5.28k   581.34    10.84k    76.26%
      3785677 requests in 1.00m, 530.71MB read
    Requests/sec:  63044.09
    Transfer/sec:      8.84MB
    

  6. Node.js with cluster

    const cluster = require("cluster");
    const http = require("http");
    const numCPUs = require("os").cpus().length;
    
    if (cluster.isMaster) {
      console.log(`Master ${process.pid} is running`);
    
      // Fork workers.
      for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
      }
    
      cluster.on("exit", (worker, code, signal) => {
        console.log(`worker ${worker.process.pid} died`);
      });
    } else {
      // Workers can share any TCP connection
      // In this case it is an HTTP server
      http
        .createServer((req, res) => {
          res.writeHead(200);
          res.end("hello world\n");
        })
        .listen(8000);
    
      console.log(`Worker ${process.pid} started`);
    }
    

    结果:

    ➜  ~ wrk -t12 -c400 -d60s http://localhost:8004
    Running 1m test @ http://localhost:8004
      12 threads and 400 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency     8.41ms   19.78ms 778.06ms   97.72%
        Req/Sec     5.11k   729.97    23.84k    79.48%
      3650506 requests in 1.00m, 396.88MB read
    Requests/sec:  60742.62
    Transfer/sec:      6.60MB
    
  7. Node.js HTTP

    const http = require("http");
    
    http
      .createServer((req, res) => {
        res.end("Hello, World!\n");
      })
      .listen(8001);
    

    结果:

    ➜  ~ wrk -t12 -c400 -d60s http://localhost:8005
    Running 1m test @ http://localhost:8005
      12 threads and 400 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency    15.08ms    2.47ms 237.96ms   95.22%
        Req/Sec     2.19k   226.48     8.01k    94.90%
      1569242 requests in 1.00m, 197.54MB read
    Requests/sec:  26113.64
    Transfer/sec:      3.29MB