The JS runtimes
Published in

The JS runtimes

Deno vs Node vs Go performance: Response time comparison for API Proxy

Introduction

In the performance series of this publication (Deno World), we’ve seen a number of performance comparisons between Deno & Node.js, and Deno & Go. The earlier articles covered a breadth of metrics including time taken, requests per second, mean, median, 3rd quantile, 4th quantile, CPU, & memory usage. The comparison tests included hello world, echo name, simple form data, complex form data, file server, etc.

In this article, we’ll use an API proxy program to compare a single metric, i.e. response time. The response time is a very important metric for front facing asynchronous runtimes like Deno. Some examples are serving UI (especially for SPAs), providing REST APIs, etc. For a good user experience, the response time needs to stay low. We’ll compare the response times between Deno, Node.js, and Go. Deno & Node are interpreted, but Go is compiled, therefore we’ll expect the response time to be quite low for Go when compared to the other two. Let’s find out.

Configuration

Environment

The performance comparison is executed on the following environment:

  • System: MacBook Pro with M1 processor & 16G of memory
  • Local: The SUT and tester are on the same system
  • Deno version: 1.16.4
  • Node version: 16.13.1
  • Go version: 1.17.5

All the above are the latest at the time of writing.

Test data

There is a mock API service that’s going to be used by both Deno & Node.js proxies. The mock API service generates a random UUID and sends it back to the caller. The mock API service is written in Deno. Here is the code of the mock API service:

import { listenAndServe } from "https://deno.land/std/http/mod.ts";listenAndServe(
":3001",
() =>
new Response(JSON.stringify({ randomId: crypto.randomUUID() }),{
headers: {
"content-type": "application/json",
},
}),
);

Code

Here is the code of an API proxy server that

  • Receives a request from the caller (no body in the request)
  • Sends an HTTP request to the mock server
  • Gets a response back from the mock server & parses it to get randomId
  • Sends the randomId to its caller in a JSON encoded repsonse body

The Node.js’s & Go’s proxy is built only on native code, while the Deno proxy uses listenAndServe from Deno’s standard library. The listenAndServe API is recommended for most of the users, rather than using a combination of listen & serveHttp.

Settings

The performance test is executed in following configurations:

  • 10 concurrent connections to run a total of ~5K requests
  • 25 concurrent connections to run a total of ~5K requests
  • 50 concurrent connections to run a total of ~5K requests

As the client & server are colocated, the total requests are kept under 10K. If it exceeds that number, the system calls start throwing errors like ‘too many open files’, ‘can not assign requested address’, etc.

Measurements

Unlike earlier articles, where we focused on a variety of measurements, this article focuses only on response time. We’ll plot four type of graphs for each of the test configuration:

  • Scatterplot to show all the data points
  • Boxplot to show the distribution across five key areas: min, q1, median, q3, max, and outliers. A great introduction of a boxplot is here.
  • Barplot to show median specifically
  • Barplot to show maximum specifically

Results

As mentioned earlier, a total of 10K requests are executed for each test configuration. This is because the system quickly runs out of resources.

The red line in the box plot is the median

Here are the graphs for all the test configurations:

10

25

50

Conclusion

Being similar, Deno & Node’s performance is comparable. Go’s median response time is the best.

This story is a part of the exclusive medium publication on Deno: Deno World.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store