Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plotting 2000x2000 image is slow. A large amount of allocations is created. #1412

Closed
MariusDrulea opened this issue Oct 28, 2021 · 5 comments

Comments

@MariusDrulea
Copy link
Contributor

MariusDrulea commented Oct 28, 2021

I have started to use GLMakie more intensively and noticed it is slow for the images I use. Bellow you can find a sample code.
heatmap, plot, image all takes about 0.15 seconds per call, and I have about 10 images to show, so this leads to 1.5 seconds, quite a lot. You can notice the amount of allocations per call: 1.00 M allocations: 50.724 MiB. Looks like each value of the 2000x2000 image is copied and allocated into something else. This can be done in a more optimal way, a single allocation for all the values at once. I am currently debugging the issue, any hint is welcome.

julia> using GLMakie

julia> x = rand(2000, 2000)

julia> @time heatmap(x)
 57.865795 seconds (111.09 M allocations: 6.655 GiB, 2.91% gc time)

julia> @time heatmap(x)
  0.168210 seconds (1.01 M allocations: 50.836 MiB)

julia> @time heatmap(x)
  0.225403 seconds (1.01 M allocations: 50.836 MiB)

julia> @time plot(x)
  0.349197 seconds (1.29 M allocations: 67.006 MiB, 54.55% compilation time)

julia> @time plot(x)
  0.170603 seconds (1.00 M allocations: 50.724 MiB)

julia> @time plot(x)
  0.180033 seconds (1.00 M allocations: 50.724 MiB)

julia> @time plot(x)
  0.192559 seconds (1.00 M allocations: 50.724 MiB)

julia> @time image(x)
  1.026312 seconds (2.70 M allocations: 153.704 MiB, 3.97% gc time, 81.16% compilation time)

julia> @time image(x)
  0.152998 seconds (1.02 M allocations: 51.078 MiB)

julia> @time image(x)
  0.159101 seconds (1.02 M allocations: 51.078 MiB)
@MariusDrulea
Copy link
Contributor Author

Extra motivation: in python the imshow takes 0.04 seconds, 4 times faster. Not acceptable!

import matplotlib.pyplot as plt
import numpy as np
import timeit

def plot_fun():
    for i in range(10):
        x = np.random.rand(2000, 2000)
        fig = plt.figure()
        s = timeit.default_timer()
        plt.imshow(x)
        t = timeit.default_timer() - s
        print("imshow runtime", t)
        fig.savefig("C:\\temp\\plot_fun_py.jpg")    
plot_fun()

imshow runtime 0.04574070000000008
imshow runtime 0.04205860000000006
imshow runtime 0.045397200000000026
imshow runtime 0.04289670000000001
imshow runtime 0.04194360000000019
imshow runtime 0.04642109999999988
imshow runtime 0.045330400000000104
imshow runtime 0.04728399999999944
imshow runtime 0.04116490000000006

@SimonDanisch
Copy link
Member

So, the creation of plots isn't fully optimized in Makie yet, since it hasn't been as important yet.

Note, that if you want to update/animate a plot use:

x = rand(2000, 2000)
figure, ax, plot = heatmap(x)
plot[1] = rand(2000, 2000)

Which should be pretty fast... Although I just see it's around 0.013566s which still seems quite slow...
Setting colorrange brings it down to 0.006:

figure, ax, plot_obj = heatmap(x, colorrange=(0, 1))
x = rand(2000, 2000)
@time plot_obj[1] = x;

That seems like calculating the extrema from the matrix is surprisingly slow (0.005137s... sounds fair maybe?)...

@MariusDrulea
Copy link
Contributor Author

MariusDrulea commented Oct 28, 2021

No display of figure

@SimonDanisch I tried your update/animation suggestion I indeed get 0.006 in 75% of the cases, and 0.015 in 25% of cases. It is a lot better than my 0.15 seconds, thanks for this solution.
Further remark:
I get the runtimes above if I don't display the figure at all (which is ok-ish, when I run lots of images I don't look at them while running). In this case there are 25 allocations, a total of 15.2 MiB that have to be handled by gc. Whenever gc reacts, we get 0.015 instead of 0.006. This means there is still room for improvements here.

using GLMakie

fig, ax, plot_obj = heatmap(rand(2000, 2000), colorrange=(0,1))
# display(fig) ----- no displayed figure

for i in 1:30
    x = rand(2000, 2000)
    @time plot_obj[1] = x
end
0.171773 seconds (292.28 k allocations: 32.986 MiB, 96.33% compilation time)
  0.005916 seconds (25 allocations: 15.260 MiB)
  0.022587 seconds (25 allocations: 15.260 MiB, 71.88% gc time)
  0.006172 seconds (25 allocations: 15.260 MiB)
  0.006220 seconds (25 allocations: 15.260 MiB)
  0.018146 seconds (25 allocations: 15.260 MiB, 64.25% gc time)
  0.006559 seconds (25 allocations: 15.260 MiB)
  0.006702 seconds (25 allocations: 15.260 MiB)
  0.014866 seconds (25 allocations: 15.260 MiB, 50.48% gc time)
  0.005968 seconds (25 allocations: 15.260 MiB)
  0.007214 seconds (25 allocations: 15.260 MiB)
  0.014658 seconds (25 allocations: 15.260 MiB, 54.58% gc time)
  0.007301 seconds (25 allocations: 15.260 MiB)
  0.006131 seconds (25 allocations: 15.260 MiB)

Display

If I display the figure, I get significantly higher runtimes, 0.01 to 0.25 seconds. 0.25 seconds in 25% of cases looks like a lot. Displaying figures seem to be very slow.

using GLMakie

fig, ax, plot_obj = heatmap(rand(2000, 2000), colorrange=(0,1))
display(fig)

for i in 1:10
    x = rand(2000, 2000)
    @time plot_obj[1] = x
end
0.334499 seconds (548.87 k allocations: 47.618 MiB, 2.85% gc time, 96.58% compilation time)
  0.022241 seconds (68 allocations: 15.292 MiB)
  0.224067 seconds (68 allocations: 15.292 MiB)
  0.137049 seconds (68 allocations: 15.292 MiB, 9.77% gc time)
  0.133526 seconds (68 allocations: 15.292 MiB)
  0.010081 seconds (68 allocations: 15.292 MiB)
  0.259269 seconds (68 allocations: 15.292 MiB, 2.92% gc time)
  0.010244 seconds (68 allocations: 15.292 MiB)
  0.010104 seconds (68 allocations: 15.292 MiB)
  0.253172 seconds (68 allocations: 15.292 MiB, 3.21% gc time)

Display in REPL vs GPU

When I display a single 2000x2000 image in the REPL, the GPU stays close to 100% most of the time. To me this looks like an over-use of GPU. Too high update frequency?
image

@SimonDanisch
Copy link
Member

When I display a single 2000x2000 image in the REPL, the GPU stays close to 100% most of the time. To me this looks like an over-use of GPU. Too high update frequency?

You could actually try using your own renderloop:

Create a function like this one https://github.com/JuliaPlots/Makie.jl/blob/master/GLMakie/src/rendering.jl#L17=,
change the render criteria from time based to whenever you update the image and then replace the renderloop with:

GLMakie.set_window_config!(renderloop=your_renderloop)

@SimonDanisch
Copy link
Member

With #2336 we now have an on demand renderloop as the default!
Also, heatmap is much slower then image, which mostly supports the same color mapping options, but only treats the matrix as an rectangle in space, which makes the implementation much faster (heatmap creates rectangles for every pixel).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants