Furor Teutonicus blog | over | volg | teuto | lyme | archief | doneer | todo
🕰️
  ⬩  
✍️ Evert Mouw
  ⬩  
⏱️ 2 min

Julia performs badly for a single Mandlebrot creation

On fortran90.org is a “Python Fortran Rosetta Stone” page with a Mandelbrot Set code comparison. I’m still learning Julia, so I tried to code a similar short program using Julia.

Maybe I did something wrong… but for me, Julia took more CPU time, more memory, and more code verbosity than the Python example. For array masking, even Fortran was less verbose than Julia. Note that array masking is building for Numpy and Fortran; I did not try MaskedArrays.jl so probably the Julia code can be made less verbose, although that adds another dependency. I doubt if time and memory consumption can be brought down.

Language user system elapsed %CPU maxresident(k)
Fortran 0.03 0.01 0:00.05 94 34280
Python 2.47 0.03 0:02.53 99 121848
Julia 3.94 0.20 0:04.10 100 503796
The resulting Mandelbrot graph.

I did change the original code from fortran90.org a bit to make the algorithms even more similar (comparable). Below, the code is shown as an image with a few comments – I hope you have a wide monitor. All my files, code, scripts and whatnot are available as download: mandelbrot_comparison.zip

The very friendly people in the Discord chat channel “Humans of Julia” made a few comments on my code, so I could learn and improve. Thanks guys! (Updated code below the picture.) Also see this gist!

A Mandlebrot graph: Julia, Python and Fortran compared.

mandelbrot_improved.jl

include("functions.jl")

# Const and type declarations didn't do much to make the code speedier,
# but they added a lot of verbosity to the code, so I removed those again.

ITERATIONS = 100
DENSITY = 1000
x_min, x_max = -2.68, 1.32
y_min, y_max = -1.5, 1.5

x,y = meshgrid(LinRange(x_min, x_max, DENSITY), LinRange(y_min, y_max, DENSITY))
c = x + (y)im
z = copy(c)
fractal = fill(255,DENSITY,DENSITY)

mask = [abs(α)  10 for α in z] # initial fill
# for loop contents improved by devin.jl
for n in 1:ITERATIONS
    println("Iteration ", n)
    @. mask = abs(z)  10
    z[mask] .^= 2
    z[mask] .+= c[mask]
    @. fractal[abs(z) > 10 && fractal == 255] = 254 * (n-1) ÷ ITERATIONS
end

logfractal = log.(fractal) #devin.jl

println("Saving...")
savetxt("fractal_jl.dat", logfractal)
savetxt("coord_jl.dat", [ x_min, x_max, y_min, y_max ] )

functions.jl

# meshgrid function by Chris Rackauckas, 2016
# https://groups.google.com/g/julia-users/c/83Pfg9HGhGQ/m/9G_0wi-GBQAJ
function meshgrid(a,b)
    x = a' .* ones(length(b))
    y = ones(length(a))' .* b
    return x,y
end

# stylistic improvement by Jacob
savetxt(filename, array) = open(filename) do io
    for row in eachrow(array)
        line = replace(string(row), "[" => "", "]" => "", "," => "")
        println(io, line)
    end
end