Speed measurement
Here we will compare the speed of plotting UnfoldMakie with MNE (Python) and EEGLAB (MATLAB).
Three cases are measured:
- Single topoplot
- Topoplot series with 50 topoplots
- Topoplott animation with 50 timestamps
Note that the results of benchmarking on your computer and on Github may differ.
using UnfoldMakie
using TopoPlots
using BenchmarkTools
using Observables
using CairoMakie
using Statistics
using PythonPlot;
using PyMNE; CondaPkg Found dependencies: /home/runner/.julia/packages/CondaPkg/8GjrP/CondaPkg.toml
CondaPkg Found dependencies: /home/runner/.julia/packages/PyMNE/AlJE6/CondaPkg.toml
CondaPkg Found dependencies: /home/runner/.julia/packages/PythonCall/83z4q/CondaPkg.toml
CondaPkg Found dependencies: /home/runner/.julia/packages/PythonPlot/oS8x4/CondaPkg.toml
CondaPkg Dependencies already up to dateData input
dat, positions = TopoPlots.example_data()
df = UnfoldMakie.eeg_array_to_dataframe(dat[:, :, 1], string.(1:length(positions)));Topoplots
UnfoldMakie.jl
@benchmark plot_topoplot(dat[:, 320, 1]; positions = positions)BenchmarkTools.Trial: 55 samples with 1 evaluation per sample.
Range (min … max): 57.626 ms … 1.519 s ┊ GC (min … max): 0.00% … 95.79%
Time (median): 64.721 ms ┊ GC (median): 0.00%
Time (mean ± σ): 91.143 ms ± 196.116 ms ┊ GC (mean ± σ): 29.03% ± 12.92%
▄ ▄▁ ▁▄ ▄▁ ▁ ▁▁ ▁ ▁▁▄ █
▆▁▁▁▁▁▁▁▆▁▁▁▁▁▁▁▆▆▁▁▆▆▁▁▆▁▁▁▆▆█▆██▆██▆██▆█▆██▁█▆▁███▁▁▁▆█▁▁▆ ▁
57.6 ms Histogram: frequency by time 68.6 ms <
Memory estimate: 11.82 MiB, allocs estimate: 194629.UnfoldMakie.jl with DelaunayMesh
@benchmark plot_topoplot(
dat[:, 320, 1];
positions = positions,
topo_interpolation = (; interpolation = DelaunayMesh()),
)BenchmarkTools.Trial: 66 samples with 1 evaluation per sample.
Range (min … max): 58.942 ms … 831.681 ms ┊ GC (min … max): 0.00% … 92.12%
Time (median): 65.355 ms ┊ GC (median): 0.00%
Time (mean ± σ): 76.691 ms ± 94.388 ms ┊ GC (mean ± σ): 15.14% ± 11.34%
▁ █ ▃ ▁ ▃
▄▁▁▁▁▁▁▄▁▄▁▄▁▁▄▄▄▁▄▇▁▇▄▇▇▇▄▇▄▇█▇▇█▁▇▇█▁█▄▇▁▄▄█▇▄▄▁▇▁▁▁▁▁▁▁▁▄ ▁
58.9 ms Histogram: frequency by time 70.4 ms <
Memory estimate: 11.82 MiB, allocs estimate: 194636.MNE
posmat = collect(reduce(hcat, [[p[1], p[2]] for p in positions])')
pypos = Py(posmat).to_numpy()
pydat = Py(dat[:, 320, 1])
@benchmark begin
f = PythonPlot.figure()
PyMNE.viz.plot_topomap(
pydat,
pypos,
sphere = 1.1,
extrapolate = "box",
cmap = "RdBu_r",
sensors = false,
contours = 6,
)
f.show()
endBenchmarkTools.Trial: 321 samples with 1 evaluation per sample.
Range (min … max): 13.417 ms … 234.645 ms ┊ GC (min … max): 0.00% … 0.00%
Time (median): 13.945 ms ┊ GC (median): 0.00%
Time (mean ± σ): 15.581 ms ± 17.425 ms ┊ GC (mean ± σ): 0.00% ± 0.00%
▁▆█▇▅▁
██████▅▅▁▄▄▄▅▁▁▄▁▁▁▁▁▁▁▁▁▁▁▁▁▄▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▄ ▆
13.4 ms Histogram: log(frequency) by time 24.3 ms <
Memory estimate: 3.30 KiB, allocs estimate: 98.Topoplot series
Note that UnfoldMakie and MNE have different defaults for displaying topoplot series. UnfoldMakie in plot_topoplot averages over time samples. MNE in plot_topopmap displays single samples without averaging.
UnfoldMakie.jl
@benchmark begin
plot_topoplotseries(
df;
bin_num = 50,
positions = positions,
axis = (; xlabel = "Time windows [s]"),
)
endBenchmarkTools.Trial: 2 samples with 1 evaluation per sample.
Range (min … max): 2.397 s … 2.935 s ┊ GC (min … max): 0.00% … 12.02%
Time (median): 2.666 s ┊ GC (median): 6.61%
Time (mean ± σ): 2.666 s ± 380.701 ms ┊ GC (mean ± σ): 6.61% ± 8.50%
█ █
█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█ ▁
2.4 s Histogram: frequency by time 2.94 s <
Memory estimate: 351.67 MiB, allocs estimate: 5078390.MNE
easycap_montage = PyMNE.channels.make_standard_montage("standard_1020")
ch_names = pyconvert(Vector{String}, easycap_montage.ch_names)[1:64]
info = PyMNE.create_info(PyList(ch_names), ch_types = "eeg", sfreq = 1)
info.set_montage(easycap_montage)
simulated_epochs = PyMNE.EvokedArray(Py(dat[:, :, 1]), info)
@benchmark simulated_epochs.plot_topomap(1:50)BenchmarkTools.Trial: 6 samples with 1 evaluation per sample.
Range (min … max): 748.436 ms … 1.188 s ┊ GC (min … max): 0.00% … 0.00%
Time (median): 764.924 ms ┊ GC (median): 0.00%
Time (mean ± σ): 849.732 ms ± 172.583 ms ┊ GC (mean ± σ): 0.00% ± 0.00%
█▁▁ ▁ ▁
███▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█ ▁
748 ms Histogram: frequency by time 1.19 s <
Memory estimate: 2.36 KiB, allocs estimate: 68.MATLAB
Running MATLAB on a GitHub Action is not easy. So we benchmarked three consecutive executions (on a screenshot) on a server with an AMD EPYC 7452 32-core processor. Note that Github and the server we used for MATLAB benchmarking are two different computers, which can give different timing results.

Animation
The main advantage of Julia is the speed with which the figures are updated.
timestamps = range(1, 50, step = 1)
framerate = 5050UnfoldMakie with .gif
vals = vec(dat[:, :, 1])
p01, p99 = quantile(vals, [0.01, 0.99])
m = max(abs(p01), abs(p99))
cr = Float32.((-m, m))
@benchmark begin
f = Makie.Figure()
dat_obs = Observable(dat[:, 1, 1])
plot_topoplot!(f[1, 1], dat_obs, positions = positions, visual = (; contours = false, colorrange = cr),)
record(f, "topoplot_animation_UM.gif", timestamps; framerate = framerate) do t
dat_obs[] = @view(dat[:, t, 1])
end
endBenchmarkTools.Trial: 3 samples with 1 evaluation per sample.
Range (min … max): 2.153 s … 2.240 s ┊ GC (min … max): 0.00% … 1.87%
Time (median): 2.230 s ┊ GC (median): 1.88%
Time (mean ± σ): 2.208 s ± 47.506 ms ┊ GC (mean ± σ): 1.33% ± 1.15%
█ █ █
█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▁▁▁▁▁█ ▁
2.15 s Histogram: frequency by time 2.24 s <
Memory estimate: 120.53 MiB, allocs estimate: 421099.
MNE with .gif
@benchmark begin
fig, anim = simulated_epochs.animate_topomap(
times = Py(timestamps),
frame_rate = framerate,
blit = false,
image_interp = "cubic", # same as CloughTocher
)
anim.save("topomap_animation_mne.gif", writer = "ffmpeg", fps = framerate)
endBenchmarkTools.Trial: 1 sample with 1 evaluation per sample.
Single result which took 8.850 s (0.00% GC) to evaluate,
with a memory estimate of 3.03 KiB, over 96 allocations.Note, that due to some bugs in (probably) PythonCall topoplot is black and white.

This page was generated using Literate.jl.