Out of frustration and anger I decided to do compare these two framework

I don’t have anger issue but after having conversation with developers and reading on blogs etc. It seems everyone favours Node.js capabilities when it come to developing I/O applications and its irritating to hear that Java is not a good choice for it.

Here is what I have found from stackoverflow about what is I/O task and CPU intensive tasks are as the meaning of I/O heavily depends on prespective. I’m still not sure if these definition are correct. I’m open to disscuss and change them.

Please do tell if the code is not optimised or if I am testing in a wrong way.

Definitions

I/O operation : Any process than does not consume CPU cycle. Calling a database, making http calls over network etc.

CPU operations: Any process or operation that consumer CPU cycle. Password Encryption, data manipulation, sorting, filtering, merge. Basically any data manipulation with data structure.

System Config and versions

OS: Ubuntu 22.04.1 LTS 64 Bit
Memory: 32 GB
Processor: 12th Gen Intel® Core™ i7–1270P
Cores: 16

Node version : v19.3.0
Spring boot version : 3.0.2-SNAPSHOT
Java version : 18.0.2-ea

Phase I (String return)

Creating a server on node using express by copying the code from official website that will return “Hello World!”, same step with spring-boot as well. Testing the performance using Apache ab with different concurrency and requests

Observations : Nodejs was consistent in ab results, on the other hand java took time to lauch thread at first run but once the thread connections are established it becomes consistent.

Running Process: Ran Nodejs express server using pm2 for utilizing all the cores, java doesn’t need any thing for it.

Below are the results

NODEJS

❯ ab -n 1000000 -c 1000 "http://localhost:3000/"  
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>  
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/  
Licensed to The Apache Software Foundation, http://www.apache.org/  
  
Benchmarking localhost (be patient)  
Completed 100000 requests  
Completed 200000 requests  
Completed 300000 requests  
Completed 400000 requests  
Completed 500000 requests  
Completed 600000 requests  
Completed 700000 requests  
Completed 800000 requests  
Completed 900000 requests  
Completed 1000000 requests  
Finished 1000000 requests  
  
  
Server Software:          
Server Hostname:        localhost  
Server Port:            3000  
  
Document Path:          /  
Document Length:        12 bytes  
  
Concurrency Level:      1000  
Time taken for tests:   46.510 seconds  
Complete requests:      1000000  
Failed requests:        0  
Total transferred:      211000000 bytes  
HTML transferred:       12000000 bytes  
Requests per second:    21500.80 [#/sec] (mean)  
Time per request:       46.510 [ms] (mean)  
Time per request:       0.047 [ms] (mean, across all concurrent requests)  
Transfer rate:          4430.34 [Kbytes/sec] received  
  
Connection Times (ms)  
              min  mean[+/-sd] median   max  
Connect:        0   11   7.4     12      38  
Processing:    13   35  14.8     31      92  
Waiting:        1   31  15.4     28      88  
Total:         25   46  11.1     43      92  
  
Percentage of the requests served within a certain time (ms)  
  50%     43  
  66%     48  
  75%     54  
  80%     58  
  90%     64  
  95%     67  
  98%     72  
  99%     77  
 100%     92 (longest request)  

Java

❯ ab -n 1000000 -c 1000 "http://localhost:8080/"  
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>  
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/  
Licensed to The Apache Software Foundation, http://www.apache.org/  
  
Benchmarking localhost (be patient)  
Completed 100000 requests  
Completed 200000 requests  
Completed 300000 requests  
Completed 400000 requests  
Completed 500000 requests  
Completed 600000 requests  
Completed 700000 requests  
Completed 800000 requests  
Completed 900000 requests  
Completed 1000000 requests  
Finished 1000000 requests  
  
  
Server Software:          
Server Hostname:        localhost  
Server Port:            8080  
  
Document Path:          /  
Document Length:        12 bytes  
  
Concurrency Level:      1000  
Time taken for tests:   25.762 seconds  
Complete requests:      1000000  
Failed requests:        0  
Total transferred:      91000000 bytes  
HTML transferred:       12000000 bytes  
Requests per second:    38816.91 [#/sec] (mean)  
Time per request:       25.762 [ms] (mean)  
Time per request:       0.026 [ms] (mean, across all concurrent requests)  
Transfer rate:          3449.55 [Kbytes/sec] received  
  
Connection Times (ms)  
              min  mean[+/-sd] median   max  
Connect:        0   12   1.1     12      22  
Processing:     4   14   2.3     13      29  
Waiting:        1   10   2.1     10      26  
Total:         12   26   2.2     25      42  
  
Percentage of the requests served within a certain time (ms)  
  50%     25  
  66%     26  
  75%     27  
  80%     28  
  90%     29  
  95%     30  
  98%     31  
  99%     32  
 100%     42 (longest request)

Node.js Code:

const express = require('express')  
const app = express()  
const port = 3000  
  
app.get('/', (req, res) => {  
    
  res.send('Hello World!')  
})  
  
app.listen(port, () => {  
  console.log(`Example app listening on port ${port}`)  
})

Java Code:

package com.prakritidev.verma.javasever;  
  
import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.RestController;  
  
@SpringBootApplication  
public class JavaseverApplication {  
  
 public static void main(String[] args) {  
  SpringApplication.run(JavaseverApplication.class, args);  
 }  
  
}  
  
@RestController  
@RequestMapping  
class Controller {   
   
 @GetMapping(path = "/")  
 public String helloWorld() {  
  return "Hello World!";  
 }  
  
}

Phase 2 : Simple Hashing implementation (CPU Task)

Code reference: https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript

Below are the results:

Phase 2 (Hashing Implementation)

I was not expecting this result. will do some some more testing in this phase.

Java

❯ ab -n 1000000 -c 1000 "http://localhost:8080/?input=duck"  
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>  
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/  
Licensed to The Apache Software Foundation, http://www.apache.org/  
  
Benchmarking localhost (be patient)  
Completed 100000 requests  
Completed 200000 requests  
Completed 300000 requests  
Completed 400000 requests  
Completed 500000 requests  
Completed 600000 requests  
Completed 700000 requests  
Completed 800000 requests  
Completed 900000 requests  
Completed 1000000 requests  
Finished 1000000 requests  
  
  
Server Software:          
Server Hostname:        localhost  
Server Port:            8080  
  
Document Path:          /?input=duck  
Document Length:        7 bytes  
  
Concurrency Level:      1000  
Time taken for tests:   38.361 seconds  
Complete requests:      1000000  
Failed requests:        0  
Total transferred:      85000000 bytes  
HTML transferred:       7000000 bytes  
Requests per second:    26067.86 [#/sec] (mean)  
Time per request:       38.361 [ms] (mean)  
Time per request:       0.038 [ms] (mean, across all concurrent requests)  
Transfer rate:          2163.84 [Kbytes/sec] received  
  
Connection Times (ms)  
              min  mean[+/-sd] median   max  
Connect:        0   18   2.3     18      34  
Processing:     8   20   2.5     20      45  
Waiting:        1   14   2.5     14      38  
Total:         18   38   1.7     38      62  
  
Percentage of the requests served within a certain time (ms)  
  50%     38  
  66%     38  
  75%     39  
  80%     39  
  90%     40  
  95%     41  
  98%     43  
  99%     45  
 100%     62 (longest request)  

Nodejs

 ab -n 1000000 -c 1000 "http://localhost:3000/?input=duck"  
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>  
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/  
Licensed to The Apache Software Foundation, http://www.apache.org/  
  
Benchmarking localhost (be patient)  
Completed 100000 requests  
Completed 200000 requests  
Completed 300000 requests  
Completed 400000 requests  
Completed 500000 requests  
Completed 600000 requests  
Completed 700000 requests  
Completed 800000 requests  
Completed 900000 requests  
Completed 1000000 requests  
Finished 1000000 requests  
  
  
Server Software:          
Server Hostname:        localhost  
Server Port:            3000  
  
Document Path:          /?input=duck  
Document Length:        7 bytes  
  
Concurrency Level:      1000  
Time taken for tests:   105.177 seconds  
Complete requests:      1000000  
Failed requests:        0  
Total transferred:      205000000 bytes  
HTML transferred:       7000000 bytes  
Requests per second:    9507.81 [#/sec] (mean)  
Time per request:       105.177 [ms] (mean)  
Time per request:       0.105 [ms] (mean, across all concurrent requests)  
Transfer rate:          1903.42 [Kbytes/sec] received  
  
Connection Times (ms)  
              min  mean[+/-sd] median   max  
Connect:        0    2   4.7      0      36  
Processing:    12  103  21.3     99     238  
Waiting:        5  102  21.9     99     237  
Total:         26  105  19.8    100     238  
  
Percentage of the requests served within a certain time (ms)  
  50%    100  
  66%    103  
  75%    105  
  80%    107  
  90%    113  
  95%    139  
  98%    194  
  99%    209  
 100%    238 (longest request)

Node.js Code:

const express = require('express')  
const app = express()  
const port = 3000  
  
  
app.get('/', (req, res) => {  
  const { input } = req.query  
  let hash = 0,  
  i, chr;  
  if (input.length === 0) return 0;  
  for (i = 0; i < input.length; i++) {  
    chr = input.charCodeAt(i);  
    hash = ((hash << 5) - hash) + chr;  
    hash |= 0; // Convert to 32bit integer  
  }  
    
    
  console.log(String(input), hash);  
  res.send(String(hash), 200);  
})  
  
app.listen(port, () => {  
  console.log(`Example app listening on port ${port}`)  
})

Java Code :

package com.prakritidev.verma.javasever;  
  
import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.RequestParam;  
import org.springframework.web.bind.annotation.RestController;  
  
@SpringBootApplication  
public class JavaseverApplication {  
  
 public static void main(String[] args) {  
  SpringApplication.run(JavaseverApplication.class, args);  
 }  
  
}  
  
@RestController  
@RequestMapping  
class Controller {  
  
 @GetMapping(path = "/")  
 public String helloWorld(@RequestParam String input) {  
  var hash = 0;   
  var i = 0;  
  char chr;  
    if (input.length() == 0) return "0";  
  for (i = 0; i < input.length(); i++) {  
   chr = input.charAt(i);  
   hash = ((hash << 5) - hash) + chr;  
   hash |= 0; // Convert to 32bit integer  
  }  
  
  return String.valueOf(hash);  
 }  
  
}

Phase 3 : Redis set and fetch call (I/O)

… working on it

Phase 4 : Mysql insert and fetch call (I/O)

… working on it

Phase 5: Websocket implementation (I/O)

… working on it

Phase 6: Backpressure implemenration

… working on it