Closed
Description
Bug summary
Draw performance for large images has deteriorated significantly between Matplotlib 3.1 and 3.8. The biggest issue seems to arise when upgrading from 3.1. to 3.2
Code for reproduction
import cProfile
from profilehooks import profile
import tkinter as tk
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_tkagg import (FigureCanvas)
from PIL import Image
import time
class Application(tk.Frame):
SIZE = 16000
def __init__(self, master=None):
super().__init__(master)
self.figure = plt.figure('Diagram')
self.canvas = FigureCanvas(self.figure, self)
self.ax = plt.axes()
self.canvas.mpl_connect('button_press_event', self.toggle)
self.border = 0
random_image = self.image_gen()
self.image = self.ax.imshow(random_image)
self.canvas.get_tk_widget().pack(side=tk.TOP,fill=tk.BOTH, expand=1)
self.pack(side=tk.TOP,fill=tk.BOTH, expand=1)
@profile(immediate=True)
def toggle(self, event):
self.border = 0 if self.border == Application.SIZE /4 else Application.SIZE /4
self.ax.set_xlim(self.border, Application.SIZE - self.border)
self.ax.set_ylim(self.border, Application.SIZE - self.border)
t1= time.process_time()
self.canvas.draw()
print("Draw time:", time.process_time() - t1)
def image_gen(self):
black = np.zeros((int(Application.SIZE / 8) , int(Application.SIZE / 8), 3),dtype='uint8')
white = np.ones((int(Application.SIZE / 8) , int(Application.SIZE / 8), 3),dtype='uint8') * 255
col1 = np.concatenate([white, black, white, black, white, black, white, black], axis=0)
col2 = np.concatenate([black, white, black, white, black, white, black, white], axis=0)
output = np.concatenate([col1, col2, col1, col2,col1, col2, col1, col2], axis=1)
im = Image.fromarray(output, 'RGB')
return im
if __name__ == '__main__':
root = tk.Tk()
app = Application(master=root)
root.state('zoomed')
app.mainloop()
Actual outcome
When running Matplotlib 3.8.2
35152 function calls (34234 primitive calls) in 6.264 seconds
Ordered by: cumulative time, internal time, call count
List reduced from 650 to 40 due to restriction <40>
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 6.264 6.264 min_code.py:29(toggle)
1 0.000 0.000 6.255 6.255 backend_tkagg.py:9(draw)
1 0.000 0.000 6.175 6.175 backend_agg.py:381(draw)
1 0.000 0.000 6.168 6.168 artist.py:93(draw_wrapper)
130/1 0.001 0.000 6.168 6.168 artist.py:54(draw_wrapper)
1 0
804C
.000 0.000 6.168 6.168 figure.py:3134(draw)
2/1 0.000 0.000 6.161 6.161 image.py:113(_draw_list_compositing_images)
1 0.000 0.000 6.161 6.161 _base.py:3005(draw)
1 0.000 0.000 6.113 6.113 image.py:623(draw)
1 0.046 0.046 6.107 6.107 image.py:928(make_image)
1 0.041 0.041 6.061 6.061 image.py:333(_make_image)
2 3.877 1.939 3.877 1.939 image.py:215(_rgb_to_rgba)
2 0.000 0.000 2.142 1.071 image.py:160(_resample)
2 2.141 1.071 2.141 1.071 {built-in method matplotlib._image.resample}
1 0.000 0.000 0.080 0.080 backend_tkagg.py:13(blit)
1 0.000 0.000 0.080 0.080 _backend_tk.py:72(blit)
2/1 0.000 0.000 0.080 0.080 {method 'call' of '_tkinter.tkapp' objects}
1 0.000 0.000 0.080 0.080 _backend_tk.py:58(_blit)
1 0.080 0.080 0.080 0.080 {built-in method matplotlib.backends._tkagg.blit}
2 0.000 0.000 0.042 0.021 axis.py:1379(draw)
18 0.000 0.000 0.018 0.001 axis.py:287(draw)
39 0.001 0.000 0.013 0.000 text.py:915(get_window_extent)
4 0.000 0.000 0.013 0.003 axis.py:1311(_get_ticklabel_bboxes)
4 0.000 0.000 0.013 0.003 axis.py:1315(<listcomp>)
54 0.002 0.000 0.013 0.000 text.py:358(_get_layout)
43 0.001 0.000 0.012 0.000 text.py:734(draw)
6 0.000 0.000 0.010 0.002 patches.py:579(draw)
6 0.000 0.000 0.010 0.002 axis.py:1270(_update_ticks)
6 0.000 0.000 0.010 0.002 patches.py:530(_draw_paths_with_artist_properties)
6 0.000 0.000 0.009 0.001 backend_agg.py:95(draw_path)
6 0.009 0.001 0.009 0.001 {method 'draw_path' of 'matplotlib.backends._backend_agg.RendererAgg' objects}
1 0.000 0.000 0.009 0.009 {built-in method builtins.print}
4 0.000 0.000 0.009 0.002 run.py:448(write)
5 0.000 0.000 0.008 0.002 rpc.py:216(remotecall)
2 0.000 0.000 0.008 0.004 axis.py:2143(_get_tick_boxes_siblings)
5 0.000 0.000 0.008 0.002 rpc.py:246(asyncreturn)
5 0.000 0.000 0.008 0.002 rpc.py:290(getresponse)
5 0.000 0.000 0.008 0.002 rpc.py:306(_getresponse)
5 0.000 0.000 0.007 0.001 threading.py:280(wait)
4 0.000 0.000 0.007 0.002 rpc.py:606(__call__)
When running Matplotlib 3.1.3
29643 function calls (28812 primitive calls) in 3.848 seconds
Ordered by: cumulative time, internal time, call count
List reduced from 661 to 40 due to restriction <40>
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 3.848 3.848 min_code.py:29(toggle)
1 0.000 0.000 3.814 3.814 backend_tkagg.py:8(draw)
1 0.000 0.000 3.552 3.552 backend_agg.py:382(draw)
130/1 0.001 0.000 3.547 3.547 artist.py:30(draw_wrapper)
1 0.000 0.000 3.547 3.547 figure.py:1661(draw)
2/1 0.000 0.000 3.540 3.540 image.py:117(_draw_list_compositing_images)
1 0.000 0.000 3.540 3.540 _base.py:2573(draw)
1 0.000 0.000 3.494 3.494 image.py:595(draw)
1 0.091 0.091 3.490 3.490 image.py:872(make_image)
1 0.002 0.002 3.399 3.399 image.py:255(_make_image)
2 3.192 1.596 3.192 1.596 image.py:163(_rgb_to_rgba)
1 0.000 0.000 0.240 0.240 _backend_tk.py:58(blit)
1 0.235 0.235 0.235 0.235 {built-in method matplotlib.backends._tkagg.blit}
2 0.204 0.102 0.205 0.102 {built-in method matplotlib._image.resample}
2 0.000 0.000 0.040 0.020 axis.py:1195(draw)
1 0.000 0.000 0.034 0.034 {built-in method builtins.print}
4 0.000 0.000 0.034 0.008 run.py:354(write)
5 0.000 0.000 0.033 0.007 rpc.py:217(remotecall)
5 0.000 0.000 0.032 0.006 rpc.py:247(asyncreturn)
5 0.000 0.000 0.032 0.006 rpc.py:291(getresponse)
5 0.000 0.000 0.032 0.006 rpc.py:307(_getresponse)
5 0.000 0.000 0.032 0.006 threading.py:263(wait)
10 0.032 0.003 0.032 0.003 {method 'acquire' of '_thread.lock' objects}
2 0.026 0.013 0.026 0.013 {method 'call' of '_tkinter.tkapp' objects}
4 0.000 0.000 0.025 0.006 rpc.py:560(__getattr__)
1 0.000 0.000 0.025 0.025 rpc.py:578(__getmethods)
1 0.000 0.000 0.021 0.021 __init__.py:1178(update_idletasks)
18 0.000 0.000 0.015 0.001 axis.py:289(draw)
4 0.000 0.000 0.015 0.004 axis.py:1074(_update_ticks)
8 0.000 0.000 0.011 0.001 ticker.py:2079(__call__)
6 0.000 0.000 0.011 0.002 patches.py:563(draw)
8 0.000 0.000 0.011 0.001 ticker.py:2083(tick_values)
8 0.000 0.000 0.011 0.001 ticker.py:2019(_raw_ticks)
6 0.000 0.000 0.010 0.002 backend_agg.py:119(draw_path)
6 0.010 0.002 0.010 0.002 {method 'draw_path' of 'matplotlib.backends._backend_agg.RendererAgg' objects}
8 0.000 0.000 0.009 0.001 axis.py:56(__init__)
4 0.000 0.000 0.009 0.002 rpc.py:607(__call__)
43 0.001 0.000 0.009 0.000 text.py:655(draw)
39 0.000 0.000 0.008 0.000 text.py:852(get_window_extent)
4 0.000 0.000 0.008 0.002 axis.py:1147(_get_tick_bboxes)
Expected outcome
I would hope that performance remained reasonable consistent or improved between versions.
Additional information
I have only been able to trace this as far as _make_image. The issue seems to persist regardless of interpolation method.
Operating system
Windows 11 64-bit
Matplotlib Version
3.2 upward
Matplotlib Backend
TkAgg
Python version
3.6 for Matplotlib 3.1.3; 3.9 for Matplotlib 3.8.2
Jupyter version
n/A
Installation
conda