Search code examples
juliaglobal-variablesbenchmarking

Execution time orders of magnitude longer depending upon global definition location?


I finished writing the following program and began to do some cleanup after the debugging stage:

using BenchmarkTools

function main()
    global solution = 0
    global a = big"1"
    global b = big"1"
    global c = big"0"
    global total = 0

    while a < 100
        while b < 100
            c = a^b
            s = string(c)
            total = 0
            for i in 1:length(s)
                total = total + Int(s[i]) - 48 
            end
            if total > solution
                global solution = total
            end
            global b = b + 1
        end
        global b = 1
        global a = a + 1
    end
end

@elapsed begin
main()
end

#run @elapsed twice to ignore compilation overhead
t = @elapsed main()
print("Solution: ", solution)
t = t * 1000;
print("\n\nProgram completed in ", round.(t; sigdigits=5), " milliseconds.")

The runtime for my machine was around 150ms.

I decided to rearrange the globals to better match the typical layout of program, where globals are defined at the top:

using BenchmarkTools

global solution = 0
global a = big"1"
global b = big"1"
global c = big"0"
global total = 0

function main()

    while a < 100
        while b < 100
            c = a^b
            s = string(c)
            total = 0
            for i in 1:length(s)
                total = total + Int(s[i]) - 48 
            end
            if total > solution
                global solution = total
            end
            global b = b + 1
        end
        global b = 1
        global a = a + 1
    end
end

@elapsed begin
main()
end

#run @elapsed twice to ignore compilation overhead
t = @elapsed main()
print("Solution: ", solution)
t = t * 1000;
print("\n\nProgram completed in ", round.(t; sigdigits=5), " milliseconds.")

Making that one change for where the globals were defined reduced the runtime on my machine to 0.0042ms.

Why is the runtime so drastically reduced?


Solution

  • Don't use globals.

    Don't. Use. Globals. They are bad.

    When you define your globals outside the main function, then the second time you run your function, a already equals 100, and main() bails out before doing anything at all.

    Global variables are a bad idea, not just in Julia, but in programming in general. You can use them when defining proper constants, like π, and maybe some other specialized cases, but not for things like this.

    Let me rewrite your function without globals:

    function main_locals()
        solution = 0
        a = 1
        while a < 100
            b = 1
            c = big(1)
            while b < 100
                c *= a
                s = string(c)
                total = sum(Int, s) - 48 * length(s)
                solution = max(solution, total)
                b += 1
            end
            a += 1
        end
        return solution
    end
    

    On my laptop this is >20x faster than your version with globals defined inside the function, that is, the version that actually works. The other one doesn't work as it should, so the comparison is not relevant.

    Edit: I have even complicated this too much. The only thing you need to do is to remove all the globals from your first function, and return the solution, then it will work fine, and be almost as fast as the code I wrote:

    function main_with_globals_removed()
        solution = 0
        a = big"1"
        b = big"1"
        c = big"0"
        total = 0
    
        while a < 100
            while b < 100
                c = a^b
                s = string(c)
                total = 0
                for i in 1:length(s)
                    total = total + Int(s[i]) - 48 
                end
                if total > solution
                    solution = total
                end
                b = b + 1
            end
            b = 1
            a = a + 1
        end
        return solution # remember return!
    end
    

    Don't use globals.