Functions and Modules
Encapsulate your Julia code in functions and modules, for convenience and performance.
As Julia is an interpreted language with a JIT compiler, if we care about performance, we need to be running compiled sections of the code. And, in order to get our code compiled automatically, we need to wrap it up inside a function.
So the number one performance tip is to learn how to write a function.
Fortunately, this is very easy to do, as shown below.
As an example, lets define a function that will evaluate the first \( N \) terms of the quadratic series
function sum_series(n) x = 0 for k in 1:n x = x + (1/k)^2 end return x end
After executing that code, we can call our function from the REPL, by typing:
In Julia, we can also define a function with a single line of code. For example, by doing:
sum_series2(N) = sum(1/n^2 for n=1:N)
We can then call it just like above:
Array Functions and the Dot Operator
It often happens that we have a function which can be applied to a scalar number, but we want to apply it to an array. Of course, we could resort to a for loop to this element-wise function application. But that would be too much hassle.
Fortunately, in Julia we can easily turn a function than accepts a scalar value, and apply it element-wise to an array. The way to do this is to employ a ‘dot’ after the function’s name.
For example, lets define a scalar function ‘f’, and apply it to an array.
f(x) = 3x^3/(1+x^2) x = [2π/n for n=1:30] y = f.(x)
Failing to use the dot in the above example would have lead to an error.
A common ‘gotcha’ is that, for example, trigonometric functions (or even arithmetic functions) can also require this treatment. We have to do:
y = sin.(x)
In case we need to use the dot operator a lot of times in an expression, instead of doing
y = 2x.^2 + 3x.^5 - 2x.^8
we can resort to broadcasting the dot operator, like this
y = @. 2x^2 + 3x^5 - 2x^8
Performance Tip #1
The most basic performance tip for writing efficient Julia code is the following: Write performance-critical code inside functions.
Actually, the way Julia’s JIT compiler works, is that the first time we call a given functions with specific types of inputs, the function gets compiled. So, after the first time we call the function, it will generally run much faster.
Alternatively, we can pre-compile our function, alerting the JIT compiler of the input variable types. For example, in our example above we should alert the JIT compiler than the variable n is an integer. We can do this by running:
For additional ways of improving code speed, make sure to check out the Performance Tips section of the official documentation. Performance is actually a huge topic in Julia!
In order to put our function in a module and import it, we first create a new ``myModule.jl’‘ file, with the following content
module myModule export sum_series function sum_series(n) x = 0 for k in 1:n x = x + (1/k)^2 end return x end end
and then we can call it from an other file, by doing
Then, our module will get compiled and available. We can access the exported function, by doing
x = myModule.sum_series(100000)
This post covered the basics of how to structure computations in Julia with functions and modules.