matteo
Parmi
Codewriter

matteo
Parmi
Codewriter

The useless Fibonacci benchmark

Yesterday, don’t ask why, I came across this old rant (the author removed it some time ago) and the related response. Usually I don’t care about this type of discussions, but I wanted to try it by myself.

  • Rant summary: Node is slow for calculate the Fibonacci sequence
  • Response summary: Node is faster than ruby and python for calculate Fibonacci sequence.

Node code

function fibonacci(n) {
  if (n < 2)
    return 1;
  else
    return fibonacci(n-2) + fibonacci(n-1);
  }
var http = require('http')

var server = http.createServer(function (request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.end(fibonacci(40) + "\n");
});

server.listen(8080);

Go code

package main

import (
    "fmt"
    "net/http"
)

func FibServer(w http.ResponseWriter, req *http.Request) {
    fmt.Fprint(w, fib(40))
}

func fib(n int) int {
    if n < 2 {
        return 1
    } else {
      return fib(n-2) + fib(n-1)
    }
}

func main() {
    http.HandleFunc("/", FibServer)
    http.ListenAndServe("localhost:8080", nil)
}

The results

Node single request:

ab http://localhost:8080/

Requests per second:    0.61 [#/sec] (mean)
Time per request:       1630.410 [ms] (mean)
Time per request:       1630.410 [ms] (mean, across all concurrent requests)


Node concurrent requests

ab -n 10 -c 10 http://localhost:8080/

Requests per second:    0.56 [#/sec] (mean)
Time per request:       17758.400 [ms] (mean)
Time per request:       1775.840 [ms] (mean, across all concurrent requests)

This is perfectly reasonable, node is single threaded out of the box, so if we block the envet loop with a cpu blocking code (fibonacci), all the requests will be processed in serial.

Lets try with Go, single request:

ab http://localhost:8080

Requests per second:    1.06 [#/sec] (mean)
Time per request:       945.878 [ms] (mean)
Time per request:       945.878 [ms] (mean, across all concurrent requests)

Go concurrent requests

ab -n 10 -c 10 http://localhost:8080/

Requests per second:    1.09 [#/sec] (mean)
Time per request:       9167.776 [ms] (mean)
Time per request:       916.778 [ms] (mean, across all concurrent requests)

The first time I ran it I was disappointed….why my blazing fast go code is not so fast? Especially with parallel requests? The http go library serves each request as separate goroutine, by default the go runtime is set the limit of CPUs that can be executing simultaneously to 1 (maybe this will change in the future). This value can be modified with the env variable GOMAXPROCS.

Go concurrent requests with GOMAXPROCS=8

ab -n 10 -c 10 http://localhost:8080/

Requests per second:    4.55 [#/sec] (mean)
Time per request:       2198.161 [ms] (mean)
Time per request:       219.816 [ms] (mean, across all concurrent requests)

Mmmmmm I love the smell of parallel execution in the morning….