-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Micro optimization of plotting #26303
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
Conversation
import matplotlib
# print(matplotlib)
import pyperf
setup = """
import matplotlib.figure as mfigure
x=[1,2,3,4,5]
y=[5,6,3,3,4]
n=6
def go():
for ii in range(n):
fig = mfigure.Figure()
ax = fig.subplots()
ax.plot(x,y)
"""
runner = pyperf.Runner()
runner.timeit(name="mpl", stmt="go()", setup=setup) This may be a better bench mark script. I have concerns that the speed up is less than the std.... |
well, did you mean to plot many times to the same figure or do a plot per figure? |
new (this branch)mpl: Mean +- std dev: 796 us +- 7 us old (3.7.2 from wheels)mpl: Mean +- std dev: 845 us +- 13 us using import matplotlib
# print(matplotlib)
import pyperf
setup = """
import matplotlib.figure as mfigure
x=[1,2,3,4,5]
y=[5,6,3,3,4]
n=6
fig = mfigure.Figure()
ax = fig.subplots()
def go():
for ii in range(n):
ax.plot(x,y)
"""
runner = pyperf.Runner()
runner.timeit(name="mpl", stmt="go()", setup=setup) so I think there is a real speed up here, even if it is realitvely small against the cost of making a new |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most things make sense for sure. Some things I simply trust are faster.
A few minor comments though.
lib/matplotlib/cbook.py
Outdated
@@ -1634,6 +1634,8 @@ def _safe_first_finite(obj, *, skip_nonfinite=True): | |||
def safe_isfinite(val): | |||
if val is None: | |||
return False | |||
if isinstance(val, int): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure special-casing ints here make sense -- certainly that'll make a microbenchmark based on plotting ints faster, but at the cost of introducing a branch for all other cases (plotting floats is likely much more common, and even np.int is perhaps a more common case than python ints in real code).
If one really wants to workaround the fact that np.isfinite is relatively slow, one can instead use math.isfinite (which on a quick microbenchmark is extremely fast), taking into account the fact that it won't handle certain cases like datetimes (but will handle numpy floats and ints), so something like
try:
if math.isfinite(val): return True
except TypeError:
pass
# continue with the np.isfinite check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an excellent suggestion. On my system np.isfinite(val) if np.isscalar(val) else True
is about 900 ns, and math.isfinite(val)
(including the try-except) is 80 ns. The isinstance(val, int)
is 50 ns, but does not handle the important float case.
PR summary
In this PR some micro optimizations are applied to the matplotlib plotting. Optimizations selected are in methods that show in the profiling (cProfile).
Benchmark
on script
PR checklist