Writing Type Stable Loops

For /While loops create a local scope. Thus, if you’d like to write a function the outputs variables defined inside of the for /while loop, one must declare these variables as global beforehand, or use a let wrapper.

The contradiction I see is that Julia has a hard time compiling code with global variables. I’ve seen this in my own code as I try to achieve type stability.

My questions are:

  • How can I write loops in a way that is type stable, even though I must declare globals?
  • Will declaring a global variable outside of a for loop have a significant impact on the performance of my code?

The easiest way to do this is by writing a function, for loops should be inside functions, and you can either create the variable inside the function, or make a mutating function that takes a variable.

5 Likes

You should not use globals at all, write everything inside functions:

function myloop(y)
     x = 0
     for i in 1:10
          x += i*y
     end
     return x
end

no need for declaring anything global. And you pass the parameters required to that function.

An additional comment: In Julia a good advice for newcommers is never, ever, use the global keyword. If you feel that you cannot avoid using it, ask for advice.

5 Likes

I think the structure of my code makes this a little more tricky. Here is sample code written for legibility (I’m new, so sorry if its inefficient style). Note that each function call is type stable.

function myloop(x_mat)
    trig = 0
    cnt = 1
    T_step = 100
    tol = 1e-6
    global Q
    global x
    global ν
    global s
    global T_bar
    while trig ==0
        x_in = x_mat[:,1:T_step*cnt]
        x,ν,s = simulate_data(x_in)
        Q = compute_Q(x,ν,s)

        if  Q[end]<tol
            trig = 1
            T_bar = T_step*cnt
        else
            cnt += 1
        end

    end

    return Q, x, ν, s, T_bar
end

Thanks! Please see post above.

Pass all those “global” variables as parameters to your function. For example with a named tuple:

julia> function f(x_mat,p)
           Q, x = p.Q, p.x # In 1.7 you can write this as (; Q, x) = p
           x_mat = x_mat*Q + x
           return x_mat
       end
f (generic function with 1 method)

julia> p = (Q = 1.0, x = 2.0); x_mat = 2.0;

julia> f(x_mat,p)
4.0


2 Likes

Is it ok if they are empty? For example:

p = (Q = Matrix{Float64}, x = Float64)

An empty matrix is defined with Q = Matrix{Float64}(undef,5,5) for example. There is no such thing as an empty scalar, like a single Float64.

What exactly are you trying to do in this case?

Note that here, if x, v and s are immutable (meaning, they are not arrays), declaring them “global” or not is the same, you are just redefining them again here.

Maybe your question is of another sort: How to use the value of a variable that was created inside a loop, outside the scope of the loop. Then, there are two options: initialize the value with any value outside the loop, or declare it local (I prefer this one):

julia> function myloop()
           local m
           for i in 1:2
               m = i
           end
           return m
       end
myloop (generic function with 1 method)

julia> myloop()
2

julia> function myloop()
           m = 0 
           for i in 1:2
               m = i
           end
           return m
       end
myloop (generic function with 1 method)

julia> myloop()
2


Looking at your code there, I think is what you actually want, to declare all those variales as local to the scope of the function, to use the values they acquire in the loop and return them.

1 Like

Yes! Thank you so much for working through my ill-posed questions, and getting at the heart of the matter. This is what I was looking for, as x, v, s are all arrays.

1 Like

Nice, next step is to realize that you are allocating new arrays at every iteration of the loop, as the result of your simulate function. This will be somewhat slow. The same for Q, and for x_in. Ideally you want to allocate the arrays outside the loop and make those function mutate their values.

1 Like