Animation slower than plotting separately

Hi, I want to plot animation of the graph in which the width of every edge changes over time but the node location is given.

Here is the code generating one plot, given a simple graph G, the location dictionary of every node and the width dictionary of every edge.

function PlotGraphWithLoc(p, G::Graph, Loc::Dict, Width::Dict=Dict())
    #return a plot that plots the graph with loc
    x = Float64[]
    y = Float64[]
    if Width != Dict()
        width_max = maximum(values(Width))
        for e in edges(G)
            u,v = src(e), dst(e)
            push!(x, Loc[u][1])
            push!(x, Loc[v][1])
            push!(x, NaN)
            push!(y, Loc[u][2])
            push!(y, Loc[v][2])
            push!(y, NaN)
            plot!([Loc[u][1],Loc[v][1]],[Loc[u][2],Loc[v][2]],linecolor =:black, lw = Width[(u,v)]/width_max*3,label=nothing)
        end 
    else
        for e in edges(G)
            u,v = src(e), dst(e)
            push!(x, Loc[u][1])
            push!(x, Loc[v][1])
            push!(x, NaN)
            push!(y, Loc[u][2])
            push!(y, Loc[v][2])
            push!(y, NaN)   
            plot!([Loc[u][1],Loc[v][1]],[Loc[u][2],Loc[v][2]],linecolor =:black, lw = 1,label= nothing)
        end 
    end
    (x, y)  
end

One plot works fine, though not super fast (maybe it can be faster?). Takes about 0.6 s for plotting with this code to run.

N_x = 100
N_y = 10
N = N_x*N_y
xs = [fld(i,N_y) for i in 0:N-1]
ys = [rem(i,N_y) for i in 0:N-1]
A = zeros(N,N)
for i in 1:N, j in 1:N
    abs(xs[i]-xs[j])+abs(ys[i]-ys[j])== 1 ? A[i,j]=1 : nothing
end
G = Graph(A)
Locs=Dict([(i,[xs[i],ys[i]]) for i in 1:N]);
p=Plots.plot()
@time x_g,y_g = PlotGraphWithLoc(p,G,Locs)
scatter!([Locs[i][1] for i in 1:N_y],[Locs[i][2] for i in 1:N_y], label = nothing)
scatter!([Locs[i][1] for i in N-N_y+1:N],[Locs[i][2] for i in N-N_y+1:N], label = nothing)

However, if I want to animate this with time-dependent width dictionary, it becomes super slow. 10 frames take 25 seconds and 100 frames take 240 s.

I wonder if I did something wrong to make it super slow? Or if there is a better way to animate this?

1 Like

Couple of things I would try. First could you change Width to Union{Dict, Nothing}? Something like:

function PlotGraphWithLoc(p, G::Graph, Loc::Dict, Width = nothing)
    ...
   if Width != nothing
   ...

I believe if you do that, the compiler can optimize out that condition based on the parameter types. (Also you wouldn’t be allocating an empty Dict all the time which increases the garbage collection load.)

Next if you could pass x & y into the function so that it reuses those arrays, that will be one less thing to garbage collect. You could do something like:

function PlotGraphWithLoc(p, G::Graph, Loc::Dict, x, y, Width = nothing)
    resize!(x, 0)
    resize!(y, 0)
    ...

After that you you might want to time the following operations to see how long they are taking then look to see if you can improve them at all:

    maximum(values(Width))
    edges(G)
    src(e)
    dst(e)

Finally you might also want to time the plot! command to see how that that takes. At the end of the day you won’t be able to go faster than however long that takes.

Edit: Although if you are just calling this function only once then only the performance of src(e) and dst(e) mater, the rest wouldn’t change the performance at all…

1 Like

Thank you ! I really like your idea and after some optimization it turns out the function is fast enough, problem is either plot or/and animate. Actually the figure shows long time after plot() is done. Do you have any idea why this happens?

Looking at the code for mp4() it calling into buildanimation() which just calls ffmpeg to convert the sequence of images into a mp4. Normally I would say adjust the compression to adjust the speed of that operation but it doesn’t appear to have an option for that :frowning:.

You could try the gif/mov/webm output instead of mp4 to see if one of those is faster.

No it is not the problem of building the video. More like a problem of how Julia cache the plot. Creating a video with any format takes less than 1s (you can check this from the last line of the pic above). The second from the last line shows that the animate macro creates a large cache/garbage of 4.5G, much larger than the sum of all plots(135 M each * 10 frames)

Thank you again for discussing this problem.