Before we conclude this section, there is yet another alternative to keep the flexibility of global variables while not losing too much performance. We can call it a global variable placeholder.
As it may have become clear to you by now, Julia can generate highly optimized code whenever the type of a variable is known at compilation time. Hence, one way to solve the problem is to create a constant placeholder and store a value inside the placeholder.
Consider this code:
# Initialize a constant Ref object with the value of 10
const semi_constant = Ref(10)
function add_using_global_semi_constant(x)
return x + semi_constant[]
end
The global constant is assigned a Ref object. In Julia, a Ref object is nothing but a placeholder where the type of the enclosed object is known. You can try this in the Julia REPL:
As we can see, the value inside Ref(10) has a type of Int64 according to the type signature, Base.RefValue{Int64}. Similarly, the type of the value inside Ref("abc") is String.
To fetch the value inside a Ref object, we can just use the index operator with no argument. Hence, in the preceding code, we use semi_constant[].
What would be the performance overhead of this extra indirection? Let's benchmark the code as usual:
That's not bad. Although it is far from the optimal performance of using global constant, it is still approximately 15 times faster than using a plain global variable.
Because Ref object is just a placeholder, the underlying value can also be assigned:
In summary, the use of Ref allows us to simulate global variables without sacrificing too much performance.