You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: High CPU utilization of the macosx backend #28960
After showing interactive figure, the CPU utilization of python process went to 100%.
Code for reproduction
######################################################## Case 1: 100% cpuimportmatplotlib.pyplotaspltfig=plt.figure()
plt.plot(range(5))
plt.show()
# after closing the windowimportpandas# starting 100% CPU utilization######################################################## Case 2: 100% cpuimportmatplotlib.pyplotaspltimportpandasaspd# No CPU utilization at the momentfig=plt.figure()
df=pd.DataFrame(range(5))
plt.plot(df[0]) # with this, CPU utilization is 100%.plt.show() # the same# after closing the window, still 100%######################################################## Case 3: no issue.importmatplotlib.pyplotaspltfig=plt.figure()
plt.plot(range(5))
plt.show() # strangely, this has no problem.
Actual outcome
no problem except it consumes 100% cpu. The figure is still responsive.
Expected outcome
matplotlib backend should not consume 100% cpu.
Additional information
pandas version 2.2.3 (pip)
Certain operation (closing interactive figures) causes 100% cpu with 'macosx' backend.
Closing the figure, or calling plt.close() does not help. (backend: macosx)
# Case 1: 100% cpu
import matplotlib.pyplot as plt
fig = plt.figure()
plt.plot(range(5))
plt.show()
# after closing the window
import pandas # starting 100% CPU utilization
Are you able to reproduce this without pandas? Every line you said causes the cpu utilization is pandas related. How are you running this? As a script, in ipython/terminal, a jupyter notebook, ...
I have this symptom on 2019 intel Mac Pro, although I don't think the symptom is related to the CPU architecture.
Initially, I thought this was something in pandas code and posted in pandas, but I found no cpu utilization after changing macosx backend to qt5agg, so I left issue here.
@QuLogic@greglucas
Apology, looks like pandas has nothing to do with this problem. In the first code, even without loading pandas, just closing the figure window causing high CPU utilization.
# I ran this on terminial, interactively python (without any ipython).
$ python --version
Python 3.12.3
$ python
Python 3.12.3 (main, Jun 9 2024, 13:36:41) [Clang 15.0.0 (clang-1500.1.0.2.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import matplotlib.pyplot as plt
>>> fig = plt.figure()
>>> plt.plot(range(5))
[<matplotlib.lines.Line2D object at 0x118cddfd0>]
>>> plt.show()
>>> # after closing the window, CPU began to reach 100%.
Thanks for the detailed information @cinsk, I can now reproduce this. Bisecting it points to this commit: fd43a64
I think this is due to our "wait_for_stdin" function which checks some things in macos' native event loop and works fine while we have our event loop running (with a figure shown), but then when we close the windows and go back to the Python terminal we want to defer back to their event loop and remove ours because it is no longer running. So I think we are spinning our wheels looking for events on a loop that is no longer running and checking this indefinitely with lots of pings to raise the CPU to 100%.
This fixes the immediate issue for me I think, where we can just exit early. But I'm not sure if we should actually be setting PyOS_InputHook = NULL when we stop our event loops instead?
diff --git a/src/_macosx.m b/src/_macosx.m
index 09838eccaf..5550e4d297 100755
--- a/src/_macosx.m+++ b/src/_macosx.m@@ -52,6 +52,10 @@ static void handleSigint(int signal) {
}
static int wait_for_stdin() {
+ // Check for whether an event loop is running currently first+ // if it isn't we should exit early and do nothing+ if (![NSApp isRunning]) return;+
@autoreleasepool {
stdin_received = false;
stdin_sigint = false;
I don't think we want to go down the route of installing/uninstalling the input hook as that means we need a good way to know when to install/remove it (removing the first time there is no event loop seems reasonable and I guess we could add it when we create the first figure?). Maybe not impossible, but a bunch of complexity that can be handled effectively having a 10hz polling loop.
Activity
QuLogic commentedon Oct 10, 2024
Does the
importfinish, or is it stuck there?greglucas commentedon Oct 10, 2024
I can't reproduce this on an arm mac.
Are you able to reproduce this without pandas? Every line you said causes the cpu utilization is pandas related. How are you running this? As a script, in ipython/terminal, a jupyter notebook, ...
cinsk commentedon Oct 15, 2024
@QuLogic import finished normally.
All the code I posted were functional. Just causing high cpu utilization.
@greglucas
I have this symptom on 2019 intel Mac Pro, although I don't think the symptom is related to the CPU architecture.
Initially, I thought this was something in pandas code and posted in pandas, but I found no cpu utilization after changing
macosxbackend toqt5agg, so I left issue here.cinsk commentedon Oct 15, 2024
@QuLogic @greglucas
Apology, looks like
pandashas nothing to do with this problem. In the first code, even without loadingpandas, just closing the figure window causing high CPU utilization.cinsk commentedon Oct 15, 2024
Not sure the profiler output would help, but I attached the output of the above (too large to paste it here)
tmp2.txt
greglucas commentedon Oct 15, 2024
Thanks for the detailed information @cinsk, I can now reproduce this. Bisecting it points to this commit: fd43a64
I think this is due to our "wait_for_stdin" function which checks some things in macos' native event loop and works fine while we have our event loop running (with a figure shown), but then when we close the windows and go back to the Python terminal we want to defer back to their event loop and remove ours because it is no longer running. So I think we are spinning our wheels looking for events on a loop that is no longer running and checking this indefinitely with lots of pings to raise the CPU to 100%.
This fixes the immediate issue for me I think, where we can just exit early. But I'm not sure if we should actually be setting
PyOS_InputHook = NULLwhen we stop our event loops instead?tacaswell commentedon Oct 15, 2024
That seems like a reasonable fix to me.
https://github.com/python/cpython/blob/90b1406b881aa0a57ce3a73c402b73b79138b7e0/Parser/myreadline.c#L47-L61 is where it gets called from readline ("old" python shell) and python/cpython#120066 is where it was added for the new shell. I think that in both cases short-circuiting and returning if the event loop is not running will be OK as something on the Python side will then wait for 100ms and then call us again.
I don't think we want to go down the route of installing/uninstalling the input hook as that means we need a good way to know when to install/remove it (removing the first time there is no event loop seems reasonable and I guess we could add it when we create the first figure?). Maybe not impossible, but a bunch of complexity that can be handled effectively having a 10hz polling loop.