This blog entry is split into the following two parts:
1. Performance analysis
Before diving into the performance analysis, I want to emphasise, that performance is a not trivial topic. Footnote 1 points out some basic thoughts you should keep in mind. My hardware setup is shortly described in footnote 2.
2. My developer experience
This section is a reflection about my personal developer experience. It is split into a section for language specific features I enjoyed / missed and another section for language specific pitfalls I ran into.
Some marks in table below have additional information described by a footnote like 1J.
|Lines of Code||344||336||123||230|
|Runs without compilation||✗||✗||✓||✓|
|Write once, run anywhere||✗||✗/✓1J||✗||✓|
|First class functions||✗||✗ / ✓3J||✓||✓|
|No restrictions in file and folder names||✓||✗7J/P||✗7J/P||✓|
- J: Java's stream api has many functional features. But it felt clumsy to me, as it needs a lot of boiler code and type converting. Surprisingly for me runtime exceptions, which could be detected by static source analysis, are still possible, as shown in this unit-test. In addition function closure works only for final or effectively final variables, what was an unexpected restriction for me.
- J: In general I think classes are a nice and useful abstraction feature. But that in Java everything has to live within a class (and a package declaration) feels to me like unnecessary enforcing of boiler code, which doesn't always provide value.
- C: Implementing a generic array with dynamic length through macros was fun for me from an academical point of view. But it also felt error prone. I am glad that it is easier to implement and use generic typed utilities in the other languages.
- J: Java supports function overloading. Wether this is a good or bad feature is not topic of this blog. But it is something you can utilise for default parameter handling.
- J/P: Originally I wanted my folder structure to look like language/problemNumber/main.extension. But as packages must be valid identifiers in Java and Python and neither java nor 01 are valid identifiers, I had to change the Java pattern to java_/_01/Main.java. The python pattern had to change to python/_01/main.py. This restriction annoyed me, as I think it is unnecessary.
The following is a short list of pitfalls I stumbled over, which are not possible by all languages. The luxury of not having to worry about those, comes with a small cost in performance, which is in my opinion not of relevance in most programs.
- Pointer errors and accidentally changing "random" data stored at some memory position are possible.
- Last character of a string (pointer to chars) is always "
- As variables don't have to be declared before first usage, typos in variable usage are valid and lead to unexpected results.
- C and Java
- Silent number overflows when doing arithmetics. The size of intermediate results must be known upfront, to prevent this.
- Annoying casting of float to double and similar is necessary.
Footnote 1: The following thoughts are just some basic reasoning about performance:
First of all you have to define your performance goals. For example if you have a function as a service, the startup time of a Java VM might be too slow for you (and a full Java VM in general is probably also more expensive in its resource usage than an interpreted language). But if you have a server running 24/7, startup time is of no concern for you.
Another question is, whether you are interested in the first execution time or in the average of one million executions. An algorithm A can be faster than algorithm B if they both run once, but algorithm B can be faster in average over multiple executions if it can be better optimized by a JIT compiler.
Also, based on the version of the compiler, the runtime and underlying hardware the performance of the same program / algorithm can differ.
In addition the input data can have huge impact on the performance. I remember reading a paper about an efficient algorithm for solving the knapsack problem during my study times. The given algorithm outperformed all other given algorithms by far with the given test data. But when I ingested random generated data, it was in general way slower than the other algorithms.
Last but not least performance is probably not the ultimate goal. How much would some performance optimization improve the user experience? Can the revenue be increased or costs be decreased through some performance optimization? How much time does it take to optimize the performance? Does the optimization make future feature development or maintenance more difficult?
Footnote 2: My PC: Windows 10 OS build 19042.685, AMD Ryzen 5 1600X 3.6GHz, 16GB Ram
Used language versions:
- C: gcc (MinGW.org GCC Build-2) 9.2.0
- Java: Java(TM) SE Runtime Environment (build 15.0.1+9-18), HotSpot(TM) 64-Bit Server VM (build 15.0.1+9-18, mixed mode, sharing)
- Python: 3.9.0
- Node.js: v14.15.0