Skip to content

Freeze when setting dashed linewidth to a close-to-zero float #14498

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

Open
fk3 opened this issue Jun 8, 2019 · 16 comments · May be fixed by #14560
Open

Freeze when setting dashed linewidth to a close-to-zero float #14498

fk3 opened this issue Jun 8, 2019 · 16 comments · May be fixed by #14560

Comments

@fk3
Copy link

fk3 commented Jun 8, 2019

Bug report

Bug summary

When setting the linewidth to a value close to zero (like 1e-15) and having a dashed linestyle in a pyplot, the application freezes.

Code for reproduction

from matplotlib import pyplot as plt

x = [0,1]
y = [0,1]

p = plt.plot(x,y, ls = "--", lw = 0.1, ms = 0.1, marker = "o", alpha = 1.0)

# ok
# plt.setp(p, lw = 1e-3)

# freeze
plt.setp(p, lw = 1e-15)

plt.show()

Actual outcome

The window opens but no plot (not even axis) is shown, instead the window is not responding.

Expected outcome

Primary Expectation: The application should not freeze.
Secondary Expectation: Show either no line at all or as thin as displayable.

Matplotlib version

  • Operating system: Windows 7
  • Matplotlib version: 2.2.4 (same with 3.1.0)
  • Matplotlib backend (print(matplotlib.get_backend())): TkAgg (same with Qt5Agg/PySide2)
  • Python version: 3.6.5 32bit

installed with pip

@anntzer
Copy link
Contributor

anntzer commented Jun 10, 2019

Looks like something wrong with Agg... Likely we should investigate the lowest value Agg can draw properly and clip linewidths in the backend_agg graphicscontext?

@timhoffm
Copy link
Member

timhoffm commented Jun 16, 2019

Turns out, small linewidths require a lot of computation. When running the above code with different linewidths, the plot window appears, but is black for a certian time. During that time CPU usage is at 100%. Computation time goes up very strongly when reducing the linewidth:

lw=1e-4   # ~1s
lw=1e-5   # ~6s
lw=1e-6   # ~54s

Further notes:

  • the delay is caused by antialiasing (setting rcParams['lines.antialiased'] = False results in immediate draw).
    Edit: Just saw that without antialiasing, the linewidth is already clipped (_backend_agg.h ll.407):
            if (!gc.isaa) {
              linewidth = (linewidth < 0.5) ? 0.5 : mpl_round(linewidth);
          }
    
  • resizing the plot also lags for small linewidths.
  • with lw=1e-3 the initial draw is not noticably slower, however, resizing already feels a bit laggy.
  • for values lw=1e-2 and smaller, no visible line is drawn at all.

What to do?

From a practical point of view everything equal or smaller than lw=1e-2 is identical (not drawn). I also don't think there are practical purposes for lines smaller than 1e-2 points. It should therefore be safe to clip lw to 1e-2.

@timhoffm timhoffm linked a pull request Jun 16, 2019 that will close this issue
@WeatherGod
Copy link
Member

WeatherGod commented Jun 17, 2019 via email

@timhoffm
Copy link
Member

Even 1e-3 is not sufficient for our test cases. See #14560. Needs more investigation.

@tacaswell tacaswell added this to the v3.2.0 milestone Jul 7, 2019
@tacaswell
Copy link
Member

Why are thin lines computationally expensive?

@timhoffm
Copy link
Member

timhoffm commented Jul 7, 2019

It seems to have to do with antialiasing. But not an expert in the field, I neither know what type of antialiasing agg is using, nor how thin lines affect the performance of antialiasing.

@ImportanceOfBeingErnest
Copy link
Member

Why are thin lines computationally expensive?

I don't think thin lines are per se computationally more expensive than thick lines; it's just that dashed lines have more dashes the smaller their width. I.e. the number of dashes per unit length, and hence the number of points the renderer needs to draw, grows because there is a fixed dash-length to width ratio.

image

@timhoffm
Copy link
Member

timhoffm commented Jul 8, 2019

@ImportanceOfBeingErnest 👍 That explains it. Non-antialiased lines are clipped to a width >= 0.5

linewidth = (linewidth < 0.5) ? 0.5 : mpl_round(linewidth);

so they do not suffer from the increased number of points for small widths and thus do not have a performance issue.

@jklymak
Copy link
Member

jklymak commented Jul 8, 2019

So can we just clip the dash length?

@tacaswell
Copy link
Member

Sigh, the dash scaling is one of those rabbit holes with a 🐲 at the bottom.

We talked about clipping that scaling, but decided not to due to concerns about negatively affecting hi-dpi outputs.

attn @dopplershift as I recall you were the most vocal on not clipping the dash scaling in the run up to 2.0.

@timhoffm
Copy link
Member

timhoffm commented Jul 8, 2019

Adding an additional safety factor of 2 or 4 for hi-dpi should be enough, I mean how high can hi-dpi be compared to a standard resolution? If you are really concerned still, one could make that cutoff configurable, either in rcParams or in some global variable. That way users with extreme needs would still be able to draw any dashing correctly.

@jklymak
Copy link
Member

jklymak commented Jul 8, 2019

I agree. How high can hi dpi go? Are there really practical uses to 10,000 dpi?

@jklymak jklymak changed the title Freeze when setting pyplot linewidth to a close-to-zero float Freeze when setting dashed linewidth to a close-to-zero float Jul 8, 2019
@jklymak
Copy link
Member

jklymak commented Jul 8, 2019

So lw=1e-2 means you need to be running 7200 dpi to see that line. I think its fair to clip at 1e-2 and let the world know that Matplotlib doesn’t support higher dpi than this. Note that no fonts can be smaller than 1 point already due to the font rendering library limits, so its really hard to see why anyone would need a 1-point lettering and lines that are 100 times thinner than the font size.

@dopplershift
Copy link
Contributor

So the original issue where I was arguing against clipping was #8032. The crux of my argument back then was that dots need to be dots, and we need to make sure we keep high DPI use cases in mind.

I'm not sure where lw=1e-4 comes into practice, but If we can solve this performance issue with even a rough clip at 1e-2 (and that indeed doesn't make a difference until you're higher than 7200 dpi), then I suppose that would be ok.

@tacaswell tacaswell modified the milestones: v3.2.0, v3.3.0 Sep 5, 2019
@timhoffm timhoffm modified the milestones: v3.3.0, v3.4.0 May 24, 2020
@jwuphysics
Copy link

jwuphysics commented Jun 26, 2020

I get a similar freeze when trying to set dashes=(0, 0), e.g.,

plt.plot([0, 1], [0, 1], dashes=(0, 0))
plt.show()

I first encountered it in the Jupyter notebook environment and then replicated it in IPython. The window becomes unresponsive and my laptop fans kick in so I had to end the process.

I am using Python 3.7.3, matplotlib 3.1.1, Qt5Agg backend, and Linux Mint 19.1.

@QuLogic QuLogic modified the milestones: v3.4.0, v3.5.0 Jan 27, 2021
@QuLogic QuLogic modified the milestones: v3.5.0, v3.6.0 Aug 19, 2021
@oscargus
Copy link
Member

oscargus commented Mar 4, 2022

I get a similar freeze when trying to set dashes=(0, 0)

This part is fixed with #22569

@QuLogic QuLogic modified the milestones: v3.6.0, v3.7.0 Aug 24, 2022
@ksunden ksunden modified the milestones: v3.7.0, v3.7.1 Feb 14, 2023
@QuLogic QuLogic modified the milestones: v3.7.1, future releases Mar 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.