Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

FIX: macos: Use standard NSApp run loop in our input hook#28981

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
tacaswell merged 4 commits intomatplotlib:mainfromgreglucas:macos-pyos-input-hook
Oct 30, 2024

Conversation

@greglucas
Copy link
Contributor

PR summary

We want to defer back to Python's native input hook handling when we don't have any figures to interact with, otherwise our runloop will continue running unecessarily.

Additionally, we need to add a short runloop burst in our hotloop to avoid saturating the CPU while waiting for input. (i.e. sitting on an idle figure would keep 100% cpu while waiting for an input() event without this runloop trigger).

closes#28960

PR checklist

@greglucas
Copy link
ContributorAuthor

Running the example from#26869 on current main and waiting forinput() would produce 100% cpu too. So the fix in#27528 to remove the runloop call actually caused that 100% cpu usage because the while(True) is running too frequently and saturating the CPU.

The removal of the runloop timer was to fix this issue:#27515 I think that was the wrong "fix" of the issue, and the real way to fix that issue is to remove our own input hook and yield back to Python's terminal handling.

In all of these examples, we should be at minimal CPU usage when sitting still. I'm still not sure how we could test/verify this? But it would be nice to add something to make sure we don't regress on this in the future.

@QuLogicQuLogic added this to thev3.9.3 milestoneOct 17, 2024
src/_macosx.m Outdated
[superclose];
--FigureWindowCount;
if (!FigureWindowCount)[NSAppstop:self];
if (!FigureWindowCount)stop(NULL);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This leaks theNone reference returned fromstop (at least on versions before it became immortal).

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

👍 Added a decref to the return object now. I could change this back to the original[NSApp stop: self] and add thePyOS_InputHook = NULL call here too instead if that is better? I just figured it might be nice to try and go through one stop function everywhere we need it, although it is only in two places currently.

We want to defer back to Python's native input hook handling whenwe don't have any figures to interact with, otherwise our runloopwill continue running unecessarily.Additionally, we need to add a short runloop burst in our hotloopto avoid saturating the CPU while waiting for input. (i.e. sittingon an idle figure would keep 100% cpu while waiting for an input()event without this runloop trigger).
@tacaswell
Copy link
Member

@ksunden and I spent a while looking at this on Friday, but not sure where that landed / if he pushed anything.

@greglucas
Copy link
ContributorAuthor

Feel free to push directly here or open a separate PR and I'm happy to review it too.

@ksunden
Copy link
Member

You can see what I have been trying inhttps://github.com/ksunden/matplotlib/tree/macos-pyos-input-hook

Essentially:

  • removing the hook actually causes some problems (most notablyplt.close("all") did not actually close windows until the event loop was started again)
  • If we don't remove the hook, I don'tthink there is much reason to change around the other stopping logic here.
  • I think this PR only solves the CPU usage by virtue of inserting what effectively amounts to a sleep
    • In changing things, I went to just run the loop and adjust how we break from the loop, I discovered that it appears that the loop referenced here is not actually the main app runloop, so effectively the new line is just a sleep.
    • I can get mostly working replacing our entire double while loop setup with[NSApp run];, but run into problems after closing plots (specifically via the window's X button) wherein a single character is processed by the repl, but then it hangs with 160% CPU usage and I'm not sure where/why.
greglucas reacted with thumbs up emoji

@greglucas
Copy link
ContributorAuthor

FYI, I think v3.6.3 is where some of this logic was referenced from with the double loop and breaking out of it:

while (true) {
while (true) {
event = [NSAppnextEventMatchingMask: NSEventMaskAny
untilDate: [NSDatedistantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (!event) {break; }
[NSAppsendEvent: event];
}
CFRunLoopRun();
if (interrupted ||CFReadStreamHasBytesAvailable(stream)) {break; }
}
if (py_sigint_handler) {PyOS_setsig(SIGINT, py_sigint_handler); }
CFReadStreamUnscheduleFromRunLoop(
stream, runloop,kCFRunLoopCommonModes);
if (sigint_socket) {CFSocketInvalidate(sigint_socket); }
if (!error) {
close(channel[0]);
close(channel[1]);
}

specifically, there was the call to the cfrunloop:
CFRunLoopRun();

which is what the "sleep" you mention is from.

// Short circuit if no windows are active
// Rely on Python's input handling to manage CPU usage
// This queries the NSApp, rather than using our FigureWindowCount because that is decremented when events still
// need to be processed to properly close the windows.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This will also make us play nicer with any other OSX apps you launch from Python (which I am not aware of any others....)

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Should we use this method in theclose() method too instead of keeping track of it ourselves?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

There are some folks embedding this in C++/objc it would appear:#27147

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

That is its own bit of fun, but I think this will not intersect with that issue because the only place thePyOS_InputHook is run is from the Python repl (either readline or the new one) so if they are embedding Python in another application (which means they make an interpreter object and then stuff strings into it to run Python code) so I expect the repl will never run.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

oooh, just after I clicked comment I connected some more dots.

Yes, we probably should be using something like this inclose and than may fix#27147 .

src/_macosx.m Outdated
staticvoidlazy_init(void) {
// Run our own event loop while waiting for stdin on the Python side
// this is needed to keep the application responsive while waiting for input
PyOS_InputHook = wait_for_stdin;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

can probably move this back down so we are not re-setting it all the time?

greglucas reacted with thumbs up emoji
Copy link
ContributorAuthor

@greglucasgreglucas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Nice updates@ksunden, thanks for pushing those!

I verified that this new change fixes the slow input typing lag regression I saw before as well.

src/_macosx.m Outdated
Comment on lines 72 to 75
NSEvent* event = [NSAppnextEventMatchingMask: NSEventMaskAny
untilDate: [NSDatedistantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

We've been aligning on: it seems like, so suggest doing the same here and below after thenotificationID indent now.

The name of this function isflushEvents() but above you addedstop_with_event() is there a common form we want to use between them?

Suggested change
NSEvent* event = [NSApp nextEventMatchingMask: NSEventMaskAny
untilDate: [NSDate distantPast]
inMode: NSDefaultRunLoopMode
dequeue: YES];
NSEvent* event = [NSApp nextEventMatchingMask: NSEventMaskAny
untilDate: [NSDate distantPast]
inMode: NSDefaultRunLoopMode
dequeue: YES];

// Short circuit if no windows are active
// Rely on Python's input handling to manage CPU usage
// This queries the NSApp, rather than using our FigureWindowCount because that is decremented when events still
// need to be processed to properly close the windows.
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Should we use this method in theclose() method too instead of keeping track of it ourselves?

Copy link
Member

@tacaswelltacaswell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

But I was on a call with Kyle as we worked through this so this should probably count as less than 1 full review.

@ksundenksunden changed the titleFIX: macos: remove our custom PyOS_InputHook after our runloop stopsFIX: macos: Use standard NSApp run loop in our input hookOct 30, 2024
@greglucas
Copy link
ContributorAuthor

I have run through this with a few different test cases as well and it looks good to me and is a nice simplification. It has been significantly changed by@ksunden, so I would consider it his PR now :) I "approve", although I can not technically click the button on my own PR.

@tacaswell
Copy link
Member

I'm going to go ahead and merge this.

@tacaswelltacaswell merged commit4468488 intomatplotlib:mainOct 30, 2024
40 of 41 checks passed
meeseeksmachine pushed a commit to meeseeksmachine/matplotlib that referenced this pull requestOct 30, 2024
QuLogic added a commit that referenced this pull requestOct 31, 2024
…981-on-v3.9.xBackport PR#28981 on branch v3.9.x (FIX: macos: Use standard NSApp run loop in our input hook)
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@QuLogicQuLogicQuLogic left review comments

@tacaswelltacaswelltacaswell approved these changes

Assignees

No one assigned

Projects

None yet

Milestone

v3.9.3

Development

Successfully merging this pull request may close these issues.

[Bug]: High CPU utilization of the macosx backend

4 participants

@greglucas@tacaswell@ksunden@QuLogic

[8]ページ先頭

©2009-2025 Movatter.jp