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 PyMNE
using PythonPlot
using BenchmarkTools
using Observables
using CairoMakie
Data 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: 132 samples with 1 evaluation per sample.
Range (min … max): 33.431 ms … 357.303 ms ┊ GC (min … max): 0.00% … 89.47%
Time (median): 34.341 ms ┊ GC (median): 0.00%
Time (mean ± σ): 37.870 ms ± 28.557 ms ┊ GC (mean ± σ): 6.40% ± 7.79%
██▂ ▃
████▄█▄▄▁▄▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▆ ▄
33.4 ms Histogram: log(frequency) by time 79 ms <
Memory estimate: 9.52 MiB, allocs estimate: 142193.
UnfoldMakie.jl with DelaunayMesh
@benchmark plot_topoplot(
dat[:, 320, 1];
positions = positions,
topo_interpolation = (; interpolation = DelaunayMesh()),
)
BenchmarkTools.Trial: 141 samples with 1 evaluation per sample.
Range (min … max): 33.586 ms … 80.842 ms ┊ GC (min … max): 0.00% … 0.00%
Time (median): 34.897 ms ┊ GC (median): 0.00%
Time (mean ± σ): 35.568 ms ± 4.256 ms ┊ GC (mean ± σ): 0.00% ± 0.00%
▁█▁ ▄
▂███▆█▅▆▃▂▃▆▄▃▁▄▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▂ ▂
33.6 ms Histogram: frequency by time 49.7 ms <
Memory estimate: 9.52 MiB, allocs estimate: 142200.
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()
end
BenchmarkTools.Trial: 319 samples with 1 evaluation per sample.
Range (min … max): 11.525 ms … 636.404 ms ┊ GC (min … max): 0.00% … 0.00%
Time (median): 11.701 ms ┊ GC (median): 0.00%
Time (mean ± σ): 15.695 ms ± 46.998 ms ┊ GC (mean ± σ): 0.00% ± 0.00%
▇█▇▄ ▁▁
████▇▆▁▄▁▁▁▄▁▁▁▁███▇▄▁▁▄▁▅▁▁▁▁▁▁▁▁▄▁▁▁▄▁▁▁▁▁▁▁▁▁▁▁▄▁▄▁▁▁▁▁▁▄ ▆
11.5 ms Histogram: log(frequency) by time 17.6 ms <
Memory estimate: 4.27 KiB, allocs estimate: 134.
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]"),
)
end
BenchmarkTools.Trial: 3 samples with 1 evaluation per sample.
Range (min … max): 1.899 s … 2.033 s ┊ GC (min … max): 0.00% … 0.00%
Time (median): 1.938 s ┊ GC (median): 0.00%
Time (mean ± σ): 1.957 s ± 68.774 ms ┊ GC (mean ± σ): 0.00% ± 0.00%
█ █ █
█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█ ▁
1.9 s Histogram: frequency by time 2.03 s <
Memory estimate: 414.75 MiB, allocs estimate: 6132328.
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): 660.962 ms … 2.040 s ┊ GC (min … max): 0.00% … 0.00%
Time (median): 675.773 ms ┊ GC (median): 0.00%
Time (mean ± σ): 900.020 ms ± 558.477 ms ┊ GC (mean ± σ): 0.00% ± 0.00%
█
█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▅ ▁
661 ms Histogram: frequency by time 2.04 s <
Memory estimate: 3.25 KiB, allocs estimate: 111.
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 = 50
50
UnfoldMakie with .gif
@benchmark begin
f = Makie.Figure()
dat_obs = Observable(dat[:, 1, 1])
plot_topoplot!(f[1, 1], dat_obs, positions = positions)
record(f, "topoplot_animation_UM.gif", timestamps; framerate = framerate) do t
dat_obs[] = @view(dat[:, t, 1])
end
end
BenchmarkTools.Trial: 2 samples with 1 evaluation per sample.
Range (min … max): 3.999 s … 4.239 s ┊ GC (min … max): 0.00% … 0.00%
Time (median): 4.119 s ┊ GC (median): 0.00%
Time (mean ± σ): 4.119 s ± 170.052 ms ┊ GC (mean ± σ): 0.00% ± 0.00%
█ █
█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█ ▁
4 s Histogram: frequency by time 4.24 s <
Memory estimate: 694.07 MiB, allocs estimate: 1008880.
MNE with .gif Note that due to some bugs in (probably) CondaPkg
topoplot is blac and white.
@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)
end
BenchmarkTools.Trial: 1 sample with 1 evaluation per sample.
Single result which took 8.731 s (0.00% GC) to evaluate,
with a memory estimate of 4.18 KiB, over 158 allocations.
This page was generated using Literate.jl.