Fasthttp: uWebsockets.js outperforms fasthttp?

Created on 13 Jan 2020  路  3Comments  路  Source: valyala/fasthttp

I was going through some benchmark pages I noticed that on the the-benchmarker that two javascript libraries nanoexpress & sifrr out-perform fasthttp by 30%.

Both libraries are using the uWebSockets.js package that contains pre-compiled binaries written in c/c++.

wrk -t1 -c400 -d10s http://127.0.0.1:8080/

uWebsocketjs

// Thread Stats   Avg      Stdev     Max   +/- Stdev
// Latency   1.17ms     1.95ms  50.30ms   99.44%
// Req/Sec   195.66k    11.91k  203.75k    99.00%
var uws = require('./uws/uws.js');
var app = uws.App()
app.get('/*', (res, req) => {
  res.end('Hello World!');
})
app.listen(8080, () => {});

fasthttp

// Thread Stats   Avg      Stdev     Max   +/- Stdev
// Latency   1.70ms      496.02us   6.39ms   64.38%
// Req/Sec   120.16k     8.92k      130.69k    77.00%
package main
import "github.com/valyala/fasthttp"
func main() {
    fasthttp.ListenAndServe(":8080", func(ctx *fasthttp.RequestCtx) {
        ctx.Response.SetBodyString("Hello, World!")
    })
}

The main reason I switched from node to go is performance regarding networking, but after seeing this I'm a bit confused in how this actually works.

Or better, is it possible to beat such performance in the future with fasthttp ( re-write/go-update/tricks )

I'm interested in any thoughts regarding this topic :+1:

question

Most helpful comment

There might better performance in the future. Go 1.14 (which will be released next month) already has some improvements. fasthttp itself can't be that much faster, it already does the bare minimum.

The main issue is that Go support proper concurrency which brings some overhead. This overhead is very visible in simple benchmarks like this that don't do anything.

You can see the impact of this proper concurrency when you do something more meaningful inside the request handler. Lets for example calculate 10 MD5 hashes (which is super fast) to simulate some processing that might happen:

wrk -t1 -c400 -d10s http://127.0.0.1:8080/

uWebsocketjs

// Thread Stats   Avg      Stdev     Max   +/- Stdev
// Latency     9.36ms    3.30ms  48.12ms   67.27%
// Req/Sec    43.02k     2.47k   47.12k    74.00%
const crypto = require('crypto');
const uws = require('uWebSockets.js');

var app = uws.App()
app.get('/*', (res, req) => {
  for (let i = 0; i < 10; i++) {
    const h = crypto.createHash('md5').update('foobar').digest('hex');
    if (h !== '3858f62230ac3c915f300c664312c63f') {
      throw h;
    }
  }
  res.end('Hello World!');
})
app.listen(8080, () => {});

fasthttp

// Thread Stats   Avg      Stdev     Max   +/- Stdev
// Latency     2.82ms  363.98us   6.02ms   84.98%
// Req/Sec   140.72k     5.03k  148.43k    75.00%
package main

import (
    "crypto/md5"
    "encoding/hex"

    "github.com/valyala/fasthttp"
)

func main() {
    fasthttp.ListenAndServe(":8080", func(ctx *fasthttp.RequestCtx) {
        for i := 0; i < 10; i++ {
            b := md5.Sum([]byte("foobar"))
            h := hex.EncodeToString(b[:])
            if h != "3858f62230ac3c915f300c664312c63f" {
                panic(h)
            }
        }
        ctx.Response.SetBodyString("Hello, World!")
    })
}

All 3 comments

Actually most of us who seen The Web Framework Benchmark on GitHub knew it, it鈥檚 only useful for routes level, there are many bottleneck where Go can appear to be more performant and where Nodejs fell short: encryption, json, logging, etc.

If Go is designed for your use case then it make sense. :)

There might better performance in the future. Go 1.14 (which will be released next month) already has some improvements. fasthttp itself can't be that much faster, it already does the bare minimum.

The main issue is that Go support proper concurrency which brings some overhead. This overhead is very visible in simple benchmarks like this that don't do anything.

You can see the impact of this proper concurrency when you do something more meaningful inside the request handler. Lets for example calculate 10 MD5 hashes (which is super fast) to simulate some processing that might happen:

wrk -t1 -c400 -d10s http://127.0.0.1:8080/

uWebsocketjs

// Thread Stats   Avg      Stdev     Max   +/- Stdev
// Latency     9.36ms    3.30ms  48.12ms   67.27%
// Req/Sec    43.02k     2.47k   47.12k    74.00%
const crypto = require('crypto');
const uws = require('uWebSockets.js');

var app = uws.App()
app.get('/*', (res, req) => {
  for (let i = 0; i < 10; i++) {
    const h = crypto.createHash('md5').update('foobar').digest('hex');
    if (h !== '3858f62230ac3c915f300c664312c63f') {
      throw h;
    }
  }
  res.end('Hello World!');
})
app.listen(8080, () => {});

fasthttp

// Thread Stats   Avg      Stdev     Max   +/- Stdev
// Latency     2.82ms  363.98us   6.02ms   84.98%
// Req/Sec   140.72k     5.03k  148.43k    75.00%
package main

import (
    "crypto/md5"
    "encoding/hex"

    "github.com/valyala/fasthttp"
)

func main() {
    fasthttp.ListenAndServe(":8080", func(ctx *fasthttp.RequestCtx) {
        for i := 0; i < 10; i++ {
            b := md5.Sum([]byte("foobar"))
            h := hex.EncodeToString(b[:])
            if h != "3858f62230ac3c915f300c664312c63f" {
                panic(h)
            }
        }
        ctx.Response.SetBodyString("Hello, World!")
    })
}

@erikdubbelboer Thanks for your example!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

iowelinux picture iowelinux  路  3Comments

vearutop picture vearutop  路  3Comments

facundomedica picture facundomedica  路  4Comments

jeremyjpj0916 picture jeremyjpj0916  路  5Comments

mayocream picture mayocream  路  3Comments