I recently started to use Julia. This is because the institute mainly teaches Julia to students, and I was a tutor of Computational Many-body Physics course. So I would like to comment on something on this language based on my experience in other programming languages.

Before going further, let me briefly introduce my language history. My first programming language was Visual Basic, that I learned in 2000. Then I started C and used it for a while until I go to the University. In my undergraduate study, I experienced C++, Java, Python, assembly (x86 and MIPS), and functional programming languages such as Lisp and ML variants. I also touched Forth, Perl, PHP, and Javascript. I now mainly use C++ for solving computational problems, Python for any scripting. I am also learning Rust now.

Something that I don’t like in Julia

I first would like to say some features that I dislike in Julia.

  • Garbage collection

I hate GC. There are many different reasons, but I hate it the most because it enters some flows that the programmers haven’t written. GC is indeed one of the reasons why the community much favors Rust over D; Rust detects memory leak in a compile-time, whereas D uses GC. If Julia intended to be a script language that doesn’t care much about performance as Python, GC is fine. However, if Julia wants to be a language also widely used in a huge project, GC is indeed what makes people hesitate to move.

  • Indexing

Julia’s default array index starts from 1. I mainly solve quantum problems involving qubits and binary representation readily simplifies the code. However, 1 based indexing adds a lot of complexity in the code. For example, for \(N\) qubit state, the CZ gate acting on i-th and j-th is simply written in C++ as

for(size_t n = 0; n < (1<<N); ++n)
{
	st[n] *= (1-2*((n >> i)& 1))*(1-2*((n >> j)& 1));
}

Moving to 1-based index adds minus one and plus one here and there and makes it difficult to read and write.

  • Performance

Julia-holics often say that Julia is as fast as C++. However, It is only true when most of your code just calls library functions that is already written in C/Fortran. Such a situation often arises in linear algebra applications but not common in general programming. When the Julia code itself runs extensive operations, C/C++ equivalents are ~2x faster.

Advantages?

Then let’s talk about some advantages of Julia often advocated by the community.

  • Faster coding

It is indeed true that writing Julia codes is much easier/faster than writing C++ codes. Even for me, who have used C++ for more than ten years, writing the same code in Julia is around 1.5-2x faster. For inexperienced people, I would say up to 5x is possible. Still, I don’t think it is a good idea to write a back-bone of a large project in Julia. If you use some codebase over and over again, the performance loss dominates the advantage gained from writing faster. For example, my code for variational Monte Carlo took 2-3 months to write (albeit being updated after that) and have been running more than a year in a Linux cluster. It may have taken just one month to write if I used Julia. However, the Julia code may have required 2x computational resources to obtain the same data that I definitely want to avoid.

  • Dynamic dispatch

Even though it is possible to implement dynamic multiple dispatch in C++ (C++ frontend of Pytorch uses such an implementaion), the language itself does not support it. Compared to this, dynamic dispatch in Julia is indeed a distinct feature. However, I think this feature is not utilized often in most of the codes for computational science. I expect more than 70% of functions are just called for a single set of parameter types.

I also note that C++ can do a simmilar thing statically, i.e., it supports static multiple dispatch. In C++, overloading and template with SNIFAE can be used to caller statically choose an appropriate version of the functions. For example, the addition function that works both for all string compatible types and all integer types can be written as below.

#include <type_traits>
#include <string>
#include <iostream>
#include <sstream>

template<typename T, 
	std::enable_if_t<std::is_convertible<const T&, const std::string>::value, int> = 0>
std::string add(const T& a, const T& b)
{
	std::stringstream res;
	res << a;
	res << '+';
	res << b;
	return res.str();
}

template<typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
T add(T a, T b)
{
	return a+b;
}

int main()
{
	std::cout << add("I", "J") << std::endl; // print I+J
	std::cout << add(3, 4) << std::endl; // print 7
	return 0;
}

Because which function to call is chosen at the compile time, we also do not lose any runtime performance in C++. Of course, the trade-off of static one is that the size of executable, but it is not a big issue in many computational programming. Another problem is that SNIFAE is not really fun to write. Adding enable_if here and there really messes up the code. Fortunately, C++20 standard introduces concepts (finally) and it will get much easier to write the same program in C++20.

Overall impression

I found that Julia’s advantages span a wide range of interests. But simultaneously, there are big competitors in each subrange. If someone wants to write code fast, Python might be a better choice. If a high level of performance is required, it is challenging to defeat Fortran/C/C++.

However, I also admit that this is a bit idealized story. Lots of C++ codes written by physicists still stay in C++98, i.e., they do not use move constructors/assignment operators and smart pointers. If such features are not utilized, not only writing codes in C++ is painful, but the result is also slow. Compared to this, Julia codes written by physicists are much clear and may even faster (not because Julia is faster but their C++ code is unbelievably poorly written). Thus for someone does not want to be a programming guru and routinely write a program that just test simple idea but do not want to lose too much performance, Julia can actually be the best choice.