Server Environment Benchmarks

I recently did some tests where I benchmarked some simple HTTP requests with servers implemented in PHP, Java, Node.js and Go:

Benchmarks were run in a CentOS 7.3 (Linux 3.10.0-514.el7.x86_64) virtual machine (Intel Core i7, 4 cores @ 2.2GHz, 1GB of RAM) running in VirtualBox on a Mac laptop. Timings were taken with ApacheBench (v.2.4.6).

  • PHP v5.4.16; Apache v2.4.6
  • Java (OpenJDK) 1.8.0_131-b11; Tomcat v7.0.69 (without APR/native)
  • Node.js v6.10.3
  • Go v1.8.1

Please Note:

There’s been plenty of discussion here about the version used for PHP, the Node.js command line and possible performance optimizations through configuration and other means. And you know what - I completely agree! There are many, many factors that go into peformance and the benchmarks were only meant as a small part of the article and not intended to be some sort of authoritative measurement. If you read the article, you know it talks about I/O models - the approach used for each, that was the primary focus and the point of the read. In any case it would be very interesting to see other posts which provide an objective comparison of these environments with different version numbers and settings tweaked etc.

I’m sure all four of these environments could have signficant performance improvements by turning the vairous knobs and optimizing.


Benchmark files/scripts:

All “data.dat” or /tmp/data files are just 65536 bytes of random data.


PHP

Default apache config from CentOS 7.3

Example benchmark command: ab -n 2000 -c 300 'http://127.0.0.1:80/test.php?n=1'

test.php:

<?php

$t = file_get_contents("./data.dat", true);

$n = intval($_REQUEST['n']);
if (!$n) {
    $n = 1;
}

for ($i = 0; $i < $n; $i++) {
    $s = hash('sha256', $t);
}

print $s;

Java

Default tomcat config from CentOS 7.3

Example benchmark command: ab -n 2000 -c 300 'http://127.0.0.1:8080/sample/test.jsp?n=1'

<%@ page import="java.nio.file.Files,java.nio.file.FileSystems,java.security.MessageDigest" %>
<%!
public static String bytesToHex(byte[] in) {
    final StringBuilder builder = new StringBuilder();
    for(byte b : in) {
            builder.append(String.format("%02x", b));
    }
    return builder.toString();
}
%>
<%

String nstr = request.getParameter("n");
int n = Integer.parseInt(nstr);

String s = "";
byte[] b = Files.readAllBytes(FileSystems.getDefault().getPath("/tmp/data"));

for (int i = 0; i < n; i++) {
	MessageDigest md = MessageDigest.getInstance("SHA-256");

	md.update(b);
	byte[] d = md.digest();
	s = bytesToHex(d);
}

out.print(s);

%>

Node

Run command: node main.js

Example benchmark command: ab -n 2000 -c 300 'http://127.0.0.1:3000/test?n=1'

const http = require('http')
const fs = require('fs')
const sha256 = require('sha256')
const url = require('url')
const port = 3000

const requestHandler = (request, response) => {
	fs.readFile('/tmp/data', function(err, data) {
		if (err) { response.end("error: " + err) }
		var u = url.parse(request.url, true);
		var n = parseInt(u.query.n);
		var resstr = '';
		for (var i = 0; i < n; i++) {
			resstr = sha256(data);
		}
		response.end(resstr);
	})
}

const server = http.createServer(requestHandler)

server.listen(port, (err) => {
	if (err) {
		return console.log('uh oh', err)
	}
	console.log('listening on '+port)
})

Go

Run command: go run server.go

Example benchmark command: ab -n 2000 -c 300 'http://127.0.0.1:4000/test?n=1

package main

import (
	"io/ioutil"
	"net/http"
	"fmt"
	"crypto/sha256"
	"log"
)

func main() {

	http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
		b, err := ioutil.ReadFile("/tmp/data")
		if err != nil { panic(err) }
		s := ""
		nstr := r.FormValue("n")
		var n int
		fmt.Sscanf(nstr, "%d", &n)
		for i := 0; i < n; i++ {
			h := sha256.New()
			h.Write(b)
			s = fmt.Sprintf("%x", h.Sum(nil))
		}
		w.Write([]byte(s))
	})

	log.Fatal(http.ListenAndServe("127.0.0.1:4000", nil))
}
Share Comments
comments powered by Disqus