Can PyPlot generate plots from multiple threads?

I’m new to Julia but already impressed because Julia is 4x faster than GNU Octave at generating the approximately 6000 frames of a 3D plot animation (30 minutes versus 120 minutes) I am making. I am hoping to be able to use Julia’s Threads module to generate the animation frames even faster but my attempts at calling PyPlot from multiple threads have resulted in faults and error messages requesting that I submit a bug report. Below is a simple test program that recreates the fault. Note that test_parallel_print() works and test_parallel_plot() generates the fault.

# import Pkg; Pkg.add("PyPlot")
using Plots; pyplot();
using Printf;

function test_parallel_print()
	print("testing multithreaded print ...\n")
	
	nPrint = 10
	Threads.@threads for i = 1:nPrint
		println("i = $i on thread $(Threads.threadid())")
	end
end

function test_parallel_plot()
	print("testing multithreaded plot ...\n")
	
	nPlot = 3
	POV = Vector{Plots.Plot{Plots.PyPlotBackend}}(undef,nPlot) # Plot Object Vector
	Threads.@threads for i = 1:nPlot
		POV[i] = plot(rand(5))
		plot!(POV[i], ones(5)./i)
		fname = @sprintf("test_plot%05d.png", i);
		savefig(POV[i], fname)
	end
end

no. Many plotting libraries are stateful (for example, even if you use GR.jl, you still can’t do it). In the case of pyplot, you will also run into Python’s GIL, which means multi-threading will never work, maybe give multi-processing a shot

e.g.

(@v1.7) pkg> activate --temp
  Activating new project at `C:\Users\alexa\AppData\Local\Temp\jl_f27Tnv`

(jl_f27Tnv) pkg> add Distributed, Plots, PyPlot
[...]

julia> cd(dirname(Base.load_path()[1])) # change to project directory

julia> using Distributed

julia> addprocs(4, exeflags="--project=.")
4-element Vector{Int64}:
 2
 3
 4
 5

julia> @everywhere begin
           using Plots
           pyplot()
       end

julia> POV = pmap(1:4) do i
           plot(rand(5))
       end
4-element Vector{Plots.Plot{Plots.PyPlotBackend}}:
 Plot{Plots.PyPlotBackend() n=1}
 Plot{Plots.PyPlotBackend() n=1}
 Plot{Plots.PyPlotBackend() n=1}
 Plot{Plots.PyPlotBackend() n=1}
1 Like

Thanks, I’ll give multi-processing a shot in the next couple days.