Introduction
Today, we're delving into the world of Node.js runtimes - specifically, the showdown between Node.js 16, Node.js 18, and the shiny new Node.js 20๐.
I aim to determine which Node.js runtime offers the best performance, ultimately optimizing my overall system. Perhaps it's just my desire to use the best one, even if the results might differ by only a couple of milliseconds ๐.
โ ๏ธ Note: Before we dive in, if you're looking for ways to optimize your AWS Lambda function, you can find valuable insights here: Optimize Your AWS Lambda: Faster Means Cheaper.
Test setup
I'll be executing a Fibonacci function within each Node.js version for both available architectures (Arm and x86), while maintaining the memory limit at 128MB.
Node.js runtimes to be tested:
- Node.js 16 x86
- Node.js 16 Arm
- Node.js 18 x86
- Node.js 18 Arm
- Node.js 20 x86
- Node.js 20 Arm
This testing isn't limited to managed runtimes alone; it includes custom runtimes as well. In total, I'll be working with 12 different Lambdas.
The code for the test is as follows:
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb'); const dynamodb = new DynamoDBClient({ region: 'eu-central-1' }); const LOOP_INDEX = 34; const fibonacci = (n) => { if (n < 2) return 1; return fibonacci(n - 2) + fibonacci(n - 1); }; const sleep = (time) => new Promise((resolve) => { setTimeout(resolve, time); }); exports.handler = async () => { fibonacci(LOOP_INDEX); const promises = []; for (let index = 0; index < LOOP_INDEX; index += 1) { promises.push(sleep(100)); } await Promise.all(promises); };
Managed Runtime Cold Start Results
In the cold start category โ๏ธ, the winner is Node.js 16 with the x86_64 architecture, triumphing with more than half the average initialization time compared to versions 18 and 20 ๐
.
The key distinction lies in Node.js 16's integration of JavaScript SDK V2, while versions 18 and 20 integrate JavaScript SDK V3, which follows a different pattern.
Despite the fact that Node.js 16 is a deprecated runtime, it outperforms the latest versions, boasting an absurdly low latency that is two times less than the others.
Custom Runtime Cold Start Results
The results are nearly identical to the managed scenario; Node.js 16 clinches victory with a clear lead over the other two. Interestingly, this time, the arm64 architecture outperformed x86_64 by just a few milliseconds. Nevertheless, we must acknowledge the deserving winner ๐ฅ.
Managed Runtime Results
It's pretty straightforward; the winner is clear: ๐ Node.js 18 with the x86_64 architecture under the hood scored an average of 1250ms for over 650 invocations. Just 36ms behind, we have Node.js 20 with x86_64, securing the second position.
Interestingly, the x86_64 architecture outperforms the arm64 architectures for Node.js, with an almost 300ms difference. This distinction could translate into cost savings in the long run, making x86 a preferable choice over arm.
โ ๏ธ Note: While my benchmark indicates that x86_64 architecture performs the best, it's essential to note that results may vary depending on your workload. Therefore, I advise to test your own functions using AWS Lambda Power Tuning.
Custom Runtime Results
This time, the crown goes to Node.js 20 with the x86_64 architecture ๐, averaging a total of 1456ms. Notably, Node.js 16 is still closely trailing the latest version.
Similar to the managed runtime, there's a discernible performance gap between x86 and arm architectures, with arm exhibiting higher latency than x86.
Scoring Recap
- Node.js performs better with x86_64.
- Node.js 16 has the fastest initialization time, scoring better by a factor of 2.
- Managed runtime outperformed Custom runtime for both cold start and runtime latency.
- Node.js 18 took the lead over Node.js 20 in runtime latency, with a mere 36ms difference.
Conclusion
Will I update my Lambda to Node.js 20? Absolutely. While the shift from Node.js 18 to 20 doesn't yield a significant difference in performance optimization, Node.js 20 offers enhanced functionalities and security updates.
A genuine surprise was discovering that Node.js 16's cold start duration was twice as fast as 18 and 20. Not bad for a deprecated runtime. Perhaps there's something with the new AWS SDK causing a delay? Hopefully, they can enhance cold start times in upcoming releases.
Running this performance test was really insightful for me. if you enjoyed this article, let me know ๐.
Thank you so much for reading! ๐ Keep an eye out for more AWS related posts, and feel free to connect with me on LinkedIn ๐ https://www.linkedin.com/in/matteo-depascale/
References
Disclaimer: opinions expressed are solely my own and do not express the views or opinions of my employer.