Benchmark HTTP Server PHP Swoole vs NodeJS vs Go

3 min read

Disclaimer
Saya bekerja di AWS, semua opini adalah dari saya pribadi. (I work for AWS, my opinions are my own.)
Benchmark PHP Swoole vs NodeJS vs Go
Benchmark PHP vs NodeJS vs Go

TeknoCerdas.com – Pada kesempatan ini penulis akan melakukan benchmark HTTP server PHP Swoole vs NodeJs vs Go. Benchmark yang dilakukan adalah benchmark sederhana yaitu masing-masing bahasa pemrograman mengembalikan sebuah string “Welcome to hello world benchmark.”

Benchmark ini tidak menggambarkan kondisi nyata aplikasi pada umumnya dimana banyak I/O seperti pembacaan file atau database. Namun paling tidak benchmark ini dapat mengindikasikan bagaimana performa dasar dari setiap bahasa pemrograman.

Bagi Anda yang tidak memiliki banyak waktu untuk membaca silahkan langsung ke bagian hasil benchmark untuk konklusi dari ketiga bahasa ini.

Ini adalah artikel yang ditulis ulang. Artikel asli dapat dilihat pada halaman Medium Rio Astamal.

Sekilas Tentang PHP Swoole, NodeJS dan Go

Swoole adalah sebuah asynchronus framework berbasis co-routine untuk PHP. Swoole ditulis menggunakan C dan dan didistribusikan dalam bentuk eksensi untuk PHP. Untuk menggunakannya pengguna harus melakukan instalasi lewat pecl.

NodeJS adalah sebuah runtime Javascript yang mendukung asynchronous event-driven dan non-blocking I/O. NodeJS dibangun diatas javascript engine yang digunakan oleh Chrome yaitu V8.

Go atau Golang adalah sebuah bahasa pemrograman open source yang dikembangkan oleh Google. Go mendukung concurency secara default melalui apa yang disebut sebagai goroutine atau channel.

Konfigurasi Benchmark

Terdapat dua mesin dalam konfigurasi benchmark yang dilakukan. Satu mesin untuk hosting HTTP server yang dibuat pada masing-masing bahasa pemrograman. Satu lagi untuk menjalankan HTTP load testing menggunakan Apache Bench (ab).

Versi dari bahasa pemrograman atau runtime yang digunakan pada benchmark kali ini.

  1. PHP v7.4.5 dan Swoole v4.5.0
  2. NodeJS v12.16.3
  3. Golang v1.14.2

Benchmark ini dilakukan menggunakan layanan AWS cloud dengan tipe instance c5d.2xlarge. Berikut spesifikasi untuk kedua mesin yang digunakan.

+------------+------------------------------------------------+
| CPU        | Intel(R) Xeon(R) Platinum 8275CL CPU @ 3.00GHz |
|------------|------------------------------------------------|
| Core       | 8 cores                                        |
|------------|------------------------------------------------|
| RAM        | 16 GB                                          |
|------------|------------------------------------------------|
| Disk       | 8 GB nvme                                      |
|------------|------------------------------------------------|
| Local CIDR | 172.31.0.0/16                                  |
|------------|------------------------------------------------|
| OS         | Ubuntu 18.04 LTS                               |
+------------+------------------------------------------------+

Konfigurasi kernel yang digunakan baik untuk application server atau client.

# tee /etc/sysctl.conf
fs.file-max=2097152
net.ipv4.tcp_synack_retries=2
net.ipv4.ip_local_port_range = 2000 65535
net.ipv4.tcp_rfc1337=1
net.core.somaxconn=65535
net.core.netdev_max_backlog=65536
net.ipv4.tcp_slow_start_after_idle=0
net.ipv4.tcp_slow_start_after_idle=0
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_fin_timeout = 5
vm.swappiness = 0
EOF

Script untuk PHP Swoole

$ cat > hello.php
on('request', function ($request, $response) {
    $response->end('Welcome to hello world benchmark.');
});

echo '> Starting Swoole HTTP server...';
$http->start();

Script untuk NodeJS

$ cat > hello.js
const http = require('http');

const requestListener = function (req, res) {
  res.writeHead(200);
  res.end('Welcome to hello world benchmark.');
}

const server = http.createServer(requestListener);
process.stdout.write('> Starting Node HTTP server...')
server.listen(8082, '0.0.0.0');

Script untuk Go

$ cat > hello.go
package main

import (
  "fmt"
  "net/http"
)

func main() {
  http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w, "Welcome to hello world benchmark.")
  })

fmt.Print("> Starting Go HTTP server...")
  http.ListenAndServe("0.0.00:8083", nil)
}

Memulai Benchmark

Benchmark dijalankan secara bergantian artinya hanya ada satu server aplikasi yang jalan pada satu waktu. Sebagai contoh ketika melakukan benchmark pada PHP Swoole maka server NodeJS dan Go dimatikan. Hal ini untuk memberikan gambaran yang lebih adil terutama penggunaan memori.

Benchmark yang dilakukan adalah mengirim HTTP request dengan concurency 500 dengan total request sebanyak 500,000.

Berikut cara penulis menjalankan benchmark untuk masing-masing http server. Setiap benchmark diulang 3 kali tanpa mematikan http server. Setiap akan diulang cache memory dibersihkan dengan perintah.

$ echo 2 > sudo tee /proc/sys/vm/drop_caches

Sebelum memulai lagi penulis juga memastikan bahwa semua network socket yang ada sudah bersih dan tidak ada yang menunggu.

$ sudo netstat -ntap | grep WAIT

Jika tidak socket yang tergantung maka benchmark dapat dimulai. Hal ini untuk menghindari limit open_max_files. Karena setiap socket yang dibuka juga merupakan sebuah file di sistem operasi berbasis *NIX.

Benchmark PHP Swoole

# Pada Mesin Server
$ php hello.php
> Starting Swoole HTTP server...

# Pada Mesin Client (Load testing)
$ ab -c 500 -n 500000 http://IP_SERVER:8081/

Benchmark NodeJS

# Pada Mesin Server
$ node hello.js
> Starting Node HTTP server...

# Pada Mesin Client (Load testing)
$ ab -c 500 -n 500000 http://IP_SERVER:8082/

Benchmark Go

# Pada Mesin Server
$ go build hello.go
$ ./hello
> Starting Go HTTP server...

# Pada Mesin Client (Load testing)
$ ab -c 500 -n 500000 http://IP_SERVER:8083/

Hasil Benchmark PHP Swoole vs NodeJS vs Go

Hasil benchmark dibagi dalam tiga bagian. Satu adalah Request per Second (RPS). Kedua adalah Max CPU Usage atau penggunaan CPU maksimum. Ketiga adalah Memory Usage atau penggunaan memory.

Request per Second

Request per Second Benchmark PHP Swoole vs NodeJs vs Go
Request per Second (Semakin tinggi semakin baik)

Pemenang untuk benchmark request per second (RPS) adalah Go. Namun bersaing ketat dengan PHP Swoole.

  1. Go — 35,509 req/s
  2. PHP Swoole — 34,919 req/s
  3. NodeJS — 21,626 req/s

Max CPU Usage

Max CPU Usage Benchmark PHP Swoole vs NodeJs vs Go
Max CPU Usage (Semakin kecil semakin baik)

Pemenang untuk benchmark Max CPU Usage adalah NodeJS dengan rata-rata 20% maksimum penggunaan CPU. Dimana penggunaannya tidak sampai separuh dari PHP Swoole dan Go.

  1. NodeJS — 20%
  2. PHP Swoole — 49,33%
  3. Go — 50,67%

Memory Usage

Memory Usage Benchmark PHP Swoole vs NodeJs vs Go
System Memory Usage (Semakin kecil semakin baik)

Pemenang untuk penggunaan memory atau RAM adalah PHP Swoole. Dimana rata-rata maksimum penggunaannya 228,67 MB. Selish cukup kecil dengan Go diposisi kedua.

  1. PHP Swoole — 228,67 MB.
  2. Go — 232 MB.
  3. NodeJS — 266 MB.

Ringkasan Benchmark

Untuk urusan performa PHP Swoole dan Go bersaing cukup ketat. NodeJS tertinggal cukup jauh untuk bagian ini.

Hal yang positif dari NodeJS adalah penggunaan CPU yang cukup rendah. Ini dikarenakan NodeJS secara design adalah single-threaded sehingga tidak memanfaatkan 8 core CPU yang ada.

Penulis mencoba menjalankan NodeJS dengan mode cluster untuk multi-core processor menggunakan pm2. Namun anehnya performannya malah jauh menurun dari normal 21,626 req/s menjadi hanya 15k req/s. Entah apa yang salah dengan konfigurasi penulis.

Hal yang diluar dugaan adalah Swoole yang fantastis dan hampir setara dengan Go. Jadi jika Anda dalah PHP developer dan tidak ada waktu untuk migrasi ke Go maka Swoole adalah alternatif yang sangat menarik.

Penulis tidak menggunakan opsi keep-alive untuk lebih mensimulasikan kondisi sebenarnya dimana jumlah user diasumsikan cukup besar.