Use the correct programming language: comparing programming languages’ performance and stress tests (with figures).
TL;DR: There are performance and stress comparisons at the bottom of this article. I have also provided a public repository with the code samples I have implemented, where you can open new pull requests to keep it growing with other languages.
Many (silly?) people write posts online, trying to “force” new developers to learn THEIR preferred programming language with no real reason or fact. So I have decided to write a post about the main differences and compare them with performance and stress tests, sharing a public repository with the code and results.
Scripting (Interpreted) Programming Languages
Scripting languages (also known as “Interpreted Languages”) are a type of programming language where the code is executed line-by-line by an interpreter at runtime rather than being compiled ahead of time. This makes scripting languages particularly useful for quick, ad hoc tasks and prototyping, where you can see the results of your changes immediately without a compilation step.
Advantages of scripting languages:
- Ease of use: Scripting languages tend to have a higher level of abstraction, with more straightforward syntax and semantics, making them easier to learn and use.
- Rapid development: Since there’s no need for a compilation step, their development can often be faster, and programs can be tested and debugged immediately.
- Flexibility: Given their interpreted nature, they are more flexible and can often be embedded within other software.
However, due to their interpreted nature, they can be slower than compiled languages, and their performance may not be adequate for computation-intensive applications.
Examples of scripting languages, among others, are:
- JavaScript
- Perl
- PHP
- Ruby
Compiled Programming Languages
Compiled languages (wrongly referred to as “Programming Languages” because both scripting and compiled are programming languages) are those where the source code is translated into machine code (or byte code) by a compiler before it is run. The computer’s hardware can execute the compiled code directly, making it faster and more efficient than interpreted code.
Advantages of compiled languages:
- Performance: The compiled code tends to run faster and more efficiently because it’s been optimized by the compiler for the target hardware.
- Security: The source code in a compiled language isn’t exposed to the end user, which can provide an extra layer of security and protection.
- Resource Management: They often provide more control over system resources like memory.
However, compiled languages often require more time for development, as the source code needs to be compiled before it can be run. This can slow down the development and debugging process.
Examples of compiled languages, among others, are:
- C (and their C++ or C# or Objective C variants)
- Go (also known as GoLang)
- Rust
- Swift
Hybrid Programming Languages
There are a few cases where some hybrid programming languages are considered both types (interpreted and compiled languages) at the same time, while the fact is that they are not one or another. Still, they are categorised “in a convenient way”, given the nature of their implementation in real-world projects.
Two examples of this are Python and Java. Both languages compile their programming code to byte code, and a virtual machine then executes the resulting byte code.
Why Python is considered a “scripting language”:
Python is usually categorised as a scripting language for a few reasons:
- Execution Model: Python’s standard implementation, CPython, is an interpreter that compiles Python code to bytecode and then interprets that bytecode. While Java also compiles to bytecode, as you’ve noted, the crucial difference is that the bytecode is then typically JIT-compiled to machine code at runtime. So, while both languages use bytecode, they have different execution models.
- Use Case: Python is often employed in situations typical for scripting languages, such as data analysis, automation tasks, prototyping, and web scripting. The term “scripting language” usually refers more to the use case of the language rather than its technical characteristics.
- Ease of Use and Flexibility: Python has many characteristics of scripting languages, such as dynamic typing, ease of use, and the ability to execute code without a separate compilation step. It’s more about the quick write-run-debug cycle which scripting languages offer.
- Historical Reasons: Python was initially designed as a scripting language for the Amoeba OS and has retained the title since then.
Why Java is considered a “compiled language”:
Although Java can be referred to as a compiled language, it works slightly differently:
- Java code requires a Java compiler: Java code is written in a high-level language and then compiled by the Java compiler (javac) into a platform-independent byte code (.class file). This byte code isn’t directly executable by the machine’s hardware but is designed to be interpreted by the Java Virtual Machine (JVM), a software interpreter.
- Java compiled code needs a runtime engine: At runtime, this byte code is either interpreted or compiled into machine code by the JVM’s Just-In-Time (JIT) compiler, making Java a hybrid between compiled and interpreted languages.
- In essence, Java gets compiled twice: first to byte code and then to machine code at runtime. This two-step process allows Java to be platform-independent (thanks to the byte code) while benefiting from some of the speed advantages of compiled languages (thanks to the JIT compiler).
- Java does not result in machine code: So while Java does involve a compilation step, it isn’t compiled directly into machine code as traditional compiled languages like C or C++ are, nor is it interpreted line-by-line at runtime as traditional scripting languages like Python or JavaScript are. Instead, it falls into a middle ground between the two.
When to Use Each
[image]
Use a scripting programming language when:
- You need to develop a prototype or script quickly.
- You are building web applications or services.
- Your application involves a lot of text processing.
- Your main concern is ease of writing and readability rather than raw performance.
Use a compiled programming language when:
- Your program requires high performance or needs to handle resource-intensive tasks.
- You are building system-level software like operating systems or game engines.
- You need fine control over system resources.
- You need the extra security provided by not exposing your source code.
Performance comparison
Performance testing is a non-functional testing technique to determine the system parameters regarding responsiveness and stability under various workloads. Performance testing measures the system’s quality attributes, such as scalability, reliability, and resource usage.
My performance tests are based on a very basic HTTP JSON Response where the system does not run any operations or after running a set of mathematical, text processing, loop processing and conditional actions.
Test #1: raw “Hello World”:
{
"server": "<engine name>",
"hello": "world"
}
- JavaScript (NodeJS): 0.026 seconds
- Python: 0.035 seconds
- PHP: 0.039 seconds
- TypeScript (Deno): 0.151 seconds
- GoLang: 0.054 seconds
Test #2: very long loop iteration:
{
"server": "<engine name>",
"hello": "world",
"loop": 1_000_000_000,
"elapsedTime": <milliseconds>
}
- JavaScript (NodeJS): 0.547 seconds
- Python: 11.242 seconds
- PHP: 2.554 seconds
- TypeScript (Deno): 3.844 seconds
- GoLang: [not tested / not implemented yet]
Test #3: maths, text, loop and conditional operations:
{
"benchmark": {
"math": "<processing time in seconds>",
"string": "<processing time in seconds>",
"loops": "<processing time in seconds>",
"ifelse": "<processing time in seconds>",
"total": "<processing time in seconds>"
},
"sysinfo": {
"time": "<current date time>",
"<engine version>": "<engine version>",
"platform": "<docker linux platform>",
"server_name": "<docker container name>"
},
"version": "<benchmark tests version>"
}
- JavaScript (NodeJS): 0.183 seconds
- Python: 0.279 seconds
- PHP: 0.136 seconds
- TypeScript (Deno): 1.150 seconds
- GoLang: [not tested / not implemented yet]
Stress Tests (or Load Tests) comparison
Stress testing is a type of performance testing that checks the robustness of a system. The goal is to evaluate the system’s behaviour and stability under extreme conditions, typically by subjecting it to loads or conditions beyond its specifications or far above what it’s likely to encounter in normal usage.
My stress tests are based on the same operations described in the Performance Tests section, but in this case, I have executed a 12 thread and 400 concurrent connections using specific stress test tools:
Test #1: raw “Hello World”:
{
"server": "<engine name>",
"hello": "world"
}
- JavaScript (NodeJS): 655_012 requests (21_808.13 requests/second)
- Python: 2_477 requests (82.35 requests/second)
- PHP: 4_788 requests (159.27 requests/second)
- TypeScript (Deno): 293_889 requests (9_770.41 requests/second)
- GoLang: 1_213_271 requests (40_361.98 requests/second)
Test #2: very long loop iteration:
{
"server": "<engine name>",
"hello": "world",
"loop": 1_000_000_000,
"elapsedTime": <milliseconds>
}
- JavaScript (NodeJS): 91 requests (3.03 requests/second)
- Python: 2 requests (0.07 requests/second)
- PHP: 11 requests (0.37 requests/second)
- TypeScript (Deno): 10 requests (0.33 requests/second)
- GoLang: [not tested / not implemented yet]
Test #3: maths, text, loop and conditional operations:
{
"benchmark": {
"math": "<processing time in seconds>",
"string": "<processing time in seconds>",
"loops": "<processing time in seconds>",
"ifelse": "<processing time in seconds>",
"total": "<processing time in seconds>"
},
"sysinfo": {
"time": "<current date time>",
"<engine version>": "<engine version>",
"platform": "<docker linux platform>",
"server_name": "<docker container name>"
},
"version": "<benchmark tests version>"
}
- JavaScript (NodeJS): 326 requests (10.84 requests/second)
- Python: 140 requests (4.65 requests/second)
- PHP: 298 requests (9.91 requests/second)
- TypeScript (Deno): 0 requests (0.00 requests/second)
- GoLang: [not tested / not implemented yet]
Metrics review:
The metrics expose that asynchronous scripting languages (like JavaScript) are much faster and more resilient than synchronous programming languages (like PHP), being able to provide faster responses and supporting higher loads (such as peak times and DDoS attacks).
The same metrics also expose that not all programming languages can handle the same volume of requests. Please note that PHP drops the volume of requests significantly compared with JavaScript; however, Python cannot serve even a single response.
Still, compiled languages (like GoLang) can handle and serve double the amount of requests than scripting (interpreted) programming languages (like JavaScript).
Please bear in mind that Deno has executed TypeScript code under development mode (that needs to be converted to JavaScript code on the fly), meaning its runtime was slower when running my benchmarks. According to the Deno website, its engine is twice faster than NodeJS.
Conclusion
Please note that these figures are not a source of truth but only a mere comparison of a specific set of operations. It’s crucial that you run your own research for each programming language and use the right tool for the job.
In summary, the choice between scripting and compiled languages largely depends on your project’s specific needs and constraints. Many modern software projects even use a combination of both, choosing the right tool for each particular task.
Resources
Source code repository:
- GitHub public & open-source repository (contributions accepted): https://github.com/DavidGarciaCat/performance-and-stress-tests
- In this repository ☝️ you can find sample codes and the metrics collected to write this Medium article.
Official (or relevant) websites:
- C++: https://isocpp.org/
- C#: https://dotnet.microsoft.com/en-us/languages/csharp
- Deno: https://deno.com/runtime
- ECMAScript (JavaScript and TypeScript): https://es6.io/
- Go (GoLang): https://go.dev/
- Java: https://www.java.com/en/
- NodeJS: https://nodejs.org/en
- Perl: https://www.perl.org/
- PHP: https://www.php.net/
- Python: https://www.python.org/
- Ruby: https://www.ruby-lang.org/en/
- Rust: https://www.rust-lang.org/
- Swift: https://www.swift.org/
Images
- Header: https://www.simplilearn.com/best-programming-languages-start-learning-today-article
- Scripting languages: https://www.prepbytes.com/blog/general/what-is-interpreted-language/
- Compiled languages: https://www.simplilearn.com/tutorials/python-tutorial/understand-the-workings-of-cpython
- Hybrid languages: https://www.simplilearn.com/tutorials/python-tutorial/understand-the-workings-of-cpython
- Performance comparison: https://www.syncfusion.com/blogs/post/deliver-high-performance-charts-with-syncfusions-wpf-chart-control.aspx
- Stress tests comparison: https://teamtreehouse.com/library/introduction-to-qa-engineering/testing-charts
- Metrics review: https://www.dreamstime.com/royalty-free-stock-images-inspection-stamp-impression-grunge-aged-look-red-color-being-inspected-little-d-men-over-white-background-men-hold-magnifying-image29810949
- Conclusion: https://www.pokernews.com/strategy/using-the-right-tool-for-the-job-35152.htm