Title: [Bug]: Savefig slow with subplots · Issue #26150 · matplotlib/matplotlib · GitHub
Open Graph Title: [Bug]: Savefig slow with subplots · Issue #26150 · matplotlib/matplotlib
X Title: [Bug]: Savefig slow with subplots · Issue #26150 · matplotlib/matplotlib
Description: Bug summary There are apparently 3 problems which combine to make savefig slow: (1) The use of many sub-plots, (2) the use of bbox_inches='tight', and (3) the use of sharex='columns'. Unfortunately I need to use all three. Code for repro...
Open Graph Description: Bug summary There are apparently 3 problems which combine to make savefig slow: (1) The use of many sub-plots, (2) the use of bbox_inches='tight', and (3) the use of sharex='columns'. Unfortunately...
X Description: Bug summary There are apparently 3 problems which combine to make savefig slow: (1) The use of many sub-plots, (2) the use of bbox_inches='tight', and (3) the use of sharex='columns'...
Opengraph URL: https://github.com/matplotlib/matplotlib/issues/26150
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"[Bug]: Savefig slow with subplots","articleBody":"### Bug summary\r\n\r\nThere are apparently 3 problems which combine to make savefig slow: (1) The use of many sub-plots, (2) the use of `bbox_inches='tight'`, and (3) the use of `sharex='columns'`. Unfortunately I need to use all three.\r\n\r\n\r\n### Code for reproduction\r\n\r\n```python\r\n%matplotlib inline\r\nfrom io import BytesIO\r\nimport numpy as np\r\nfrom matplotlib.figure import Figure\r\n\r\n# Random Number Generator.\r\nrng = np.random.default_rng()\r\n\r\n# Constants.\r\nfigsize = (10, 6)\r\nncols = 3\r\nnrows = 10\r\nsize = 100\r\nsize_total = ncols * nrows * size\r\n\r\n# Figure with many subplots.\r\nfig_many = Figure(figsize=figsize)\r\naxs_many = fig_many.subplots(ncols=ncols, nrows=nrows)\r\n\r\n# Figure with many subplots and sharex='col'.\r\nfig_many_sharex = Figure(figsize=figsize)\r\naxs_many_sharex = fig_many_sharex.subplots(ncols=ncols, nrows=nrows, sharex='col')\r\n\r\n# Figure with a single axes.\r\nfig_single = Figure(figsize=figsize)\r\nax_single = fig_single.subplots()\r\n\r\n# Helper-function: Generate random line-plots in the many subplots.\r\ndef generate_fig_many(axs):\r\n for row in range(nrows):\r\n for col in range(ncols):\r\n ax = axs[row, col]\r\n x = rng.normal(loc=row+1, scale=col+1, size=size)\r\n y = rng.normal(loc=col+1, scale=row+1, size=size)\r\n x = np.sort(x)\r\n ax.plot(x, y);\r\n ax.set_yticks([])\r\n\r\n# Generate fig_many \r\ngenerate_fig_many(axs=axs_many)\r\nfig_many.tight_layout()\r\n\r\n# Generate fig_many_sharex\r\ngenerate_fig_many(axs=axs_many_sharex)\r\nfig_many_sharex.tight_layout()\r\n\r\n# Generate fig_single\r\nx = rng.normal(size=size_total)\r\ny = rng.normal(size=size_total)\r\nx = np.sort(x)\r\nax_single.plot(x, y);\r\nfig_single.tight_layout()\r\n\r\n# The following code-chunks were run in individual Jupyter cells.\r\n\r\n%%timeit\r\nstream = BytesIO()\r\nfig_single.savefig(stream, format='svg')\r\ns = stream.getvalue()\r\n# 29.2 ms ± 168 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\r\n\r\n%%timeit\r\nstream = BytesIO()\r\nfig_single.savefig(stream, format='svg', bbox_inches='tight')\r\ns = stream.getvalue()\r\n# 102 ms ± 6.03 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\r\n\r\n%%timeit\r\nstream = BytesIO()\r\nfig_many.savefig(stream, format='svg')\r\ns = stream.getvalue()\r\n# 374 ms ± 4.17 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\r\n\r\n%%timeit\r\nstream = BytesIO()\r\nfig_many.savefig(stream, format='svg', bbox_inches='tight')\r\ns = stream.getvalue()\r\n# 1.4 s ± 12.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\r\n\r\n%%timeit\r\nstream = BytesIO()\r\nfig_many_sharex.savefig(stream, format='svg')\r\ns = stream.getvalue()\r\n# 565 ms ± 5.58 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\r\n\r\n%%timeit\r\nstream = BytesIO()\r\nfig_many_sharex.savefig(stream, format='svg', bbox_inches='tight')\r\ns = stream.getvalue()\r\n# 2.22 s ± 20.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\r\n\r\n%%timeit\r\nstream = BytesIO()\r\nfig_many_sharex.savefig(stream, format='jpg', bbox_inches='tight')\r\ns = stream.getvalue()\r\n# 2.17 s ± 21 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\r\n\r\n%%timeit\r\nstream = BytesIO()\r\nfig_many_sharex.savefig(stream, format='png', bbox_inches='tight')\r\ns = stream.getvalue()\r\n# 2.19 s ± 31.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\r\n```\r\n\r\n\r\n### Actual outcome\r\n\r\nThe test-results are summarized in this table, which are all for the SVG format. A few tests are made above for JPG and PNG formats and the results are similar.\r\n\r\n| Figure | no bbox | bbox=tight | layout=constrained | layout=tight |\r\n|--------|--------:|-----------:|-------------------:|-------------:|\r\n|`fig_single` | 29 | 102 | 30 | 30 |\r\n|`fig_many` | 374 | 1,400 | 1,410 | 1,340 |\r\n|`fig_many_sharex` | 565 | 2,220 | 2,220 | 2,110 |\r\n\r\nEdit: Added time-usage for setting either `layout='constrained'` or `'tight'` when creating the `Figure` objects.\r\n\r\n### Expected outcome\r\n\r\nI would like it to run [like this](https://www.youtube.com/watch?v=gAKekhmTRaY) (you asked for a visual example).\r\n\r\n### Additional information\r\n\r\nThanks for making Matplotlib, I've been using it for many open-source projects in the past!\r\n\r\nI am currently building a web-app where Matplotlib will be generating many SVG plots on a server that is running in the cloud. My own functions for generating the data are very fast, but unfortunately the plotting itself is very slow. For example, a figure with 3 columns and 10 rows of sub-plots takes **7 seconds** to run `savefig` - even though most of the sub-plots only have a simple text-string such as \"Same as previous\", and the few other sub-plots are either line-plots or `fill_between` that are generated from just 100 data-points each.\r\n\r\nI have tried simulating this problem in the sample code above, where `fig_many` has many sub-plots, and `fig_single` has a single plot with the same total number of data-points. I also tried using a profiler on this code, but it would take me forever to try and understand what the problem is in Matplotlib's code, and whether it's even fixable.\r\n\r\nPlease tell me if it might be possible to improve the speed, or if it's not possible then please explain the technical reason, and whether there is a work-around.\r\n\r\nThanks!\r\n\r\n### Operating system\r\n\r\nKubuntu 22\r\n\r\n### Matplotlib Version\r\n\r\n3.7.1\r\n\r\n### Matplotlib Backend\r\n\r\nmodule://matplotlib_inline.backend_inline\r\n\r\n### Python version\r\n\r\n3.9.12\r\n\r\n### Jupyter version\r\n\r\n6.4.12 (through VSCode)\r\n\r\n### Installation\r\n\r\npip","author":{"url":"https://github.com/Hvass-Labs","@type":"Person","name":"Hvass-Labs"},"datePublished":"2023-06-19T13:03:30.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":13},"url":"https://github.com/26150/matplotlib/issues/26150"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:ad018e81-b32f-43fa-d818-f0c5dc3df533 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 95D2:4342D:6598BB:88F3F7:6A526940 |
| html-safe-nonce | 537febc9b1ad1bd4bbaf9f6c745661a6a8841773934583d73bf1bf0083ab45cb |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5NUQyOjQzNDJEOjY1OThCQjo4OEYzRjc6NkE1MjY5NDAiLCJ2aXNpdG9yX2lkIjoiMjQ2MzU2MzM4NDE1Njc0NDAwMCIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | c73daca14cde0246e1a420c28e3da607e0d79945d472afd32af19e5e7c1c7431 |
| hovercard-subject-tag | issue:1763504918 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/matplotlib/matplotlib/26150/issue_layout |
| twitter:image | https://opengraph.githubassets.com/c6f1fd6ed77e20f5d5d8d2696cae550d192dc3194e625bff32ae459a2ef20845/matplotlib/matplotlib/issues/26150 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/c6f1fd6ed77e20f5d5d8d2696cae550d192dc3194e625bff32ae459a2ef20845/matplotlib/matplotlib/issues/26150 |
| og:image:alt | Bug summary There are apparently 3 problems which combine to make savefig slow: (1) The use of many sub-plots, (2) the use of bbox_inches='tight', and (3) the use of sharex='columns'. Unfortunately... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | Hvass-Labs |
| hostname | github.com |
| expected-hostname | github.com |
| None | b9a586c06a05a7a86fc7e3f4dbd03e42f6869085879aa184aa6369456dbd50fb |
| turbo-cache-control | no-preview |
| go-import | github.com/matplotlib/matplotlib git https://github.com/matplotlib/matplotlib.git |
| octolytics-dimension-user_id | 215947 |
| octolytics-dimension-user_login | matplotlib |
| octolytics-dimension-repository_id | 1385122 |
| octolytics-dimension-repository_nwo | matplotlib/matplotlib |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 1385122 |
| octolytics-dimension-repository_network_root_nwo | matplotlib/matplotlib |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 07a982c1d40157c619b364352b704c3ce66bb332 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width