Async NodeJS compared to Pycnic
The purpose of these benchmarks was to test the claim that node.js's non-blocking nature is faster than other production webservers. The most popular web framework for Node.js is Express.js, by at least one order of magnitude (github stars).
While Pycnic is definitely not the most popular Python framework, this is still the Pycnic documentation.
Methods
Both tests were deployed in a production-ready fashion. Though there
may be optimizations to both (using Nginx over Gunicorn, or PM2 instead of node
) the
goal was to reasonably simulate a small-scale deployment.
- Both tests were ran with
ab -c 5000 -n 5 127.0.0.1:<port>/
- Both tests read a .json file and returned its contents.
- The Express.js test uses asynchronous code and is served by
node
. - The Pycnic test uses synchronous code, and is served by
gunicorn -w 2
.
Express.js Source
var express = require("express") var fs = require("fs") var app = express() function getFileData() { let p = new Promise( function(resolve, reject) { fs.readFile('/home/nullism/foo.json', 'utf8', function(err, data) { resolve(data) }) } ) return p } app.get("/", function(req, res) { getFileData().then( function(val) { res.end(val) } ) }) app.listen(3000, function() { console.log("Listening on port 3000") })
Pycnic Source
from pycnic.core import WSGI, Handler def get_file_data(): with open("/home/nullism/foo.json") as fh: return fh.read() class Root(Handler): def get(self): return get_file_data() class app(WSGI): routes = [("/", Root())]
Results
Express.js
This is ApacheBench, Version 2.3 <$Revision: 1706008 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient) Completed 500 requests Completed 1000 requests Completed 1500 requests Completed 2000 requests Completed 2500 requests Completed 3000 requests Completed 3500 requests Completed 4000 requests Completed 4500 requests Completed 5000 requests Finished 5000 requests Server Software: Server Hostname: 127.0.0.1 Server Port: 3000 Document Path: / Document Length: 61 bytes Concurrency Level: 5 Time taken for tests: 2.278 seconds Complete requests: 5000 Failed requests: 0 Total transferred: 795000 bytes HTML transferred: 305000 bytes Requests per second: 2195.30 [#/sec] (mean) Time per request: 2.278 [ms] (mean) Time per request: 0.456 [ms] (mean, across all concurrent requests) Transfer rate: 340.87 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.1 0 2 Processing: 1 2 11.3 2 358 Waiting: 0 2 11.3 2 358 Total: 1 2 11.3 2 358 Percentage of the requests served within a certain time (ms) 50% 2 66% 2 75% 2 80% 2 90% 2 95% 2 98% 3 99% 4 100% 358 (longest request)
Pycnic
This is ApacheBench, Version 2.3 <$Revision: 1706008 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient) Completed 500 requests Completed 1000 requests Completed 1500 requests Completed 2000 requests Completed 2500 requests Completed 3000 requests Completed 3500 requests Completed 4000 requests Completed 4500 requests Completed 5000 requests Finished 5000 requests Server Software: gunicorn/19.6.0 Server Hostname: 127.0.0.1 Server Port: 8000 Document Path: / Document Length: 61 bytes Concurrency Level: 5 Time taken for tests: 1.588 seconds Complete requests: 5000 Failed requests: 0 Total transferred: 965000 bytes HTML transferred: 305000 bytes Requests per second: 3148.74 [#/sec] (mean) Time per request: 1.588 [ms] (mean) Time per request: 0.318 [ms] (mean, across all concurrent requests) Transfer rate: 593.46 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 Processing: 0 2 0.3 1 11 Waiting: 0 1 0.2 1 11 Total: 1 2 0.3 1 11 ERROR: The median and mean for the processing time are more than twice the standard deviation apart. These results are NOT reliable. ERROR: The median and mean for the total time are more than twice the standard deviation apart. These results are NOT reliable. Percentage of the requests served within a certain time (ms) 50% 1 66% 2 75% 2 80% 2 90% 2 95% 2 98% 2 99% 2 100% 11 (longest request)
Conclusion
Though Express.js may outperform Pycnic in a single-worker scenario, this is not the case when using a production Python webserver (gunicorn with two workers in this case).
Multi-worker Pycnic outperforms Express.js, and it may be difficult to justify the additional verbosity of Node.js server-side programming.
# Express.js Requests per second: 2195.30 [#/sec] (mean) Time per request: 2.278 [ms] (mean) Time per request: 0.456 [ms] (mean, across all concurrent requests) Transfer rate: 340.87 [Kbytes/sec] received # Pycnic Requests per second: 3148.74 [#/sec] (mean) Time per request: 1.588 [ms] (mean) Time per request: 0.318 [ms] (mean, across all concurrent requests) Transfer rate: 593.46 [Kbytes/sec] received
Questions or Comments?
Feel free to open an issue on the Pycnic Github page.