Open source programming language for high-performance scientific computation
For those who are working in data sciences and computational fields, some of the commonly used programming languages would have been Python, R, Scala, Java etc. However, most of these languages are far away from C program in terms of performance. If one were to make heavy-duty computations by not compromising on performance, julia could be the immediate choice. Despite being a dynamically typed language, julia offers performance almost close to a C program.
Julia is a free, open source programming language whose core implementation is licensed under MIT license. It is relatively young and evolving. Some of the key features of the language are described below:
- Julia is dynamic, high-level, procedural, functional, garbage-collected and flexible language that is designed mainly for scientific and numerical computation and gives performance comparable to statically typed languages. It can also be used as a general purpose programming language. Programmers who are familiar with syntax of Python, Lisp, C etc. could easily start off with julia.
- List styled metaprogramming and macros – Metaprogramming is a style of writing code that would process and modify itself. In Julia, the code is represented as a syntax tree that is built out of julia’s own data structures. This makes it easy to construct and manipulate julia code from within itself. Macros are a great way to write abstracted code. They execute during the parse time of compilation and help include fragments of code before full program is actually run. Besides providing some useful in-built macros like ‘time’, which would measure time taken by a julia operation or a function call, the language also has provision for defining advanced user-defined macros. We’ll see examples of some in-built macros later in this article.
- Dynamic but fast – Julia uses LLVM based high-performance JIT compiler for compilation that makes it fast. The JIT compilation is what differentiates julia from other dynamically typed languages in terms of performance. When running a julia script, you might find the first time’s code run takes time but gets speeded up from the second time because of compilation that happened in the first time. The official site julialang.org has a mention of performance of various languages relative to C for some of commonly occurring code patterns.
- Designed for parallelism and distributed computing – Julia comes with parallel programming patterns, channels for inter-task communication, cluster managers that are responsible for launching worker processes, managing events during the worker’s lifetime and the data movement. The language provides multi-processing environment based on message passing that would allow programs to run on multiple processors. It also provides some of powerful macros like ‘spawn’ and ‘parallel’ for parallel computation.
- Interoperable with Python and C – Julia gels well with other programming languages. One could easily call python code by importing the PyCall package in julia code. C functions could be directly called.
- Julia can be interacted either via an interactive mode or run as a script – It comes with an interactive command-line REPL (Read-Evaluate-Print Loop) mode that would let you type julia expressions or code and immediately produce results similar to python’s interactive shell. Besides, one could write code in a file ending with .jl extension and run it as a script. It is also supported for run in jupyter notebook, which is a browser based UI for executing code.
- Most of the standard library in julia is again written in julia itself.
With julia 0.4 release there are many new features like,
Function call overloading where any julia object can be called like a function.
Generated functions, which are a unique and powerful form of staged programming. We’ll see an example of this at the end of this article.
Fast package loading in which modules can be saved as compiled object files to avoid re-compiling all over again.
Inline documentation, a python-like docstrings in markdown format.
Follow these below steps for julia installation:-
(1) Download the julia command line version from http://julialang.org/downloads/ based on your host OS.
(2) Installation is straight forward and can be done by referring http://julialang.org/downloads/platform.html. Once installation is done, you could launch the CLI version in terminal or command prompt by double clicking the julia icon. You’d see the julia prompt like shown in Figure 1.
This is it. You are all set to start coding in julia.
Figure 1: Julia terminal
Let me introduce you to some basic programming by starting off with a simple linear algebra example. Let’s create two 2×2 matrixes A and B, and A*B would give the matrices multiplied like shown below,
julia> A = [ 1 2
julia> B = [-4 -6
One might also write julia code in a file ending with .jl extension and run as
[Linux-bash]$ julia <file>.jl <arg1> <arg2> …
Another simple example for printing first n digits of fibonacci series using user-defined functions is as shown below,
julia> function fibonacci(n)
n = n-1
fibonacci (generic function with 1 method)
1 1 2 3 5 8 13 21 34 55
One could use in-built macros like say ‘time’ to measure time taken for running this operation as shown below. Macros in julia start with ‘@’ and is a powerful feature of the language.
julia> @time fibonacci(10)
1 1 2 3 5 8 13 21 34 55
0.003762 seconds (1.18 k allocations: 64.563 KB)
julia> @time fibonacci(20)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
0.000273 seconds (133 allocations: 5.984 KB)
The above example also demonstrates the performance improvement of code in the second run of fibonacci() due to compilation and caching done during the first time.
Another special macro that is worth mentioning is the generated macro which allows programmer to define generated functions. Generated functions generate special form of code based on type of arguments passed in the function call. A simple illustration is shown below
julia> @generated function raise2(x)
raise2 (generic function with 1 method)
Whenever raise2() is called, only the returned expression is evaluated and not the complete code that generated it. You might observe that first time when raise2() function is called, the whole of raise2() is executed but on consecutive runs the function call is replaced with only the evaluated expression and we don’t see println(“Inside raise2()”) getting executed. This is because the code for argument type integer has already been generated the first time and the same would be cached and reused on subsequent runs, which is a way of speeding up the execution. When the same function raise2() is now called with a string argument, then the code for string type argument gets generated for the first time and then the same gets reused.
This raise2() example here is not the real essence of julia’s generated functions and is being shown here only for illustration purpose. In reality, julia uses generated functions for much advanced operations like say unwinding a for loop at compile time to avoid its execution in run-time.
While there are performance advantages with julia, one thing to remember is that the language is relatively young but evolving with increasing number of contributors to the project and it has potential to evolve with ease because of it being new.
I’ve tried to cover an overview and basics of julia in this article. It may not be possible to cover the whole of language right here. I’m leaving the rest for readers to explore. Please visit for references on books and tutorials on julia.