I have recently spent quite a bit of time trying to come up with a better way to implement RESTful service that serve data in JSON. This is because most of our work these days involves writing client-side JavaScript apps that present a rich UI in the browser and then do all server-side interactions via AJAX. In pursuit of the best way to implement RESTful service on the backend, I have tried my hands at Go programming language, D programming language, Lua and of course Java and PHP. I was assuming that the Go or Lua would be much faster than PHP. Well, it turns out that at least without special optimizations, that is not the case.
I wrote the following programs to compare their performance:
<?php
header('Content-Type: application/json');
$db = new PDO('mysql:host=localhost;dbname=db;charset=utf8', 'user', 'pass');
$stmt = $db->query("SELECT * FROM node");
$result = array();
$result['list'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($result);
<?php
define('DRUPAL_ROOT', getcwd());
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
// drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
header('Content-Type: application/json');
$stmt = db_query("SELECT * FROM node");
$result = array();
$result['list'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($result);
json = require "cjson" function json_result(key, data) local result = {} result[key] = data ngx.say(json.encode(result)) end function json_error(...) local result = {} result["message"] = string.format(...) ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR ngx.say(json.encode(result)) end mysql = require "resty.mysql" local db, err = mysql:new() if not db then json_error("failed to instantiate mysql: %s", err) return end db:set_timeout(1000) -- 1 sec local ok, err, errno, sqlstate = db:connect{ host = "127.0.0.1", port = 3306, database = "db", user = "user", password = "pass", max_packet_size = 1024 * 1024 } if not ok then json_error("failed to connect: %s: %d %d", err, errno, sqlstate) return end result, err, errno, sqlstate = db:query("SELECT * FROM node") if not result then json_error("failed to query: %s: %d %d", err, errno, sqlstate) end json_result("list", result)
The above programs simply query a table and return all rows and columns from that table as a JSON array.
And then I hit the above programs with a bit of load using 'ab' HTTP load testing command. Following are the results ...
Language/API | Average time/request |
PHP PDO API | 0.519 ms |
PHP Drupal API | 4.281 ms |
LuaJIT OpenResty API with code cache ON |
0.877 ms |
LuaJIT OpenResty API with code cache OFF |
2.191 ms |
Go lang with GORM and Go-JSON-REST | 0.880 ms |
So it turns out that pure PHP PDO API version is the fastest. LuaJIT with code cache ON is a close second. LuaJIT with code cache OFF is slower. And PHP with Drupal API version is slowest because it incurs the overhead of bootstrapping Drupal. And the Go version is about same as LuaJIT (didn't include the source code for that one). I didn't bother writing one for Java, as I know that it will perform pretty well, but consume a boatload of resources.
As you can see, that PHP is still the fastest, even without APC. But in reality, the raw performance of the program itself won't matter, because running from across the internet, the network latency will add about another 100ms. So I'm not sure why I'd switch away from PHP just yet. PHP is not my favorite language. But at this time, I can't justify changing my whole programming stack without real benefits of switching.
tags: drupal php go lua