Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Automatic event loop start/stop when not showing anything #607

Merged
merged 7 commits into from
Jan 14, 2022

Conversation

IanButterworth
Copy link
Collaborator

@IanButterworth IanButterworth commented Jan 12, 2022

As reported in #503 & JuliaLang/julia#35552 loading Gtk in a julia session basically ruins the performance of the session from there-on out, especially threading.

Fixing the core issue appears to be intrinsic to how julia runs threads, and might take a while to fix(?)

So... this PR:

  • Introduces Gtk.enable_eventloop(b::Bool) for start/stopping the GLib event loop, which prevents blocking other julia tasks/threads
  • By default starts Gtk in idle state with no event loop
  • The event loop is then started with any call to show or showall so that rendering always starts when something is shown
  • Then stops the eventloop when all show-ed widgets have been destroyed
  • Previous behavior can be regained via setting env var "GTK_AUTO_IDLE" to "false", or Gtk.AUTO_IDLE[] = false

Big caveat:
I followed this for guidance on what uv_prepare and uv_check should return to set idle state, but it's all effectively just setting the timeout to 0, resulting in a cpu thread at 100%. It would clearly be better to start/stop the main thread, so it would be good to implement what @StefanMathis demoed here #503 (comment)
Update: This now fully starts/stops the eventloop, so no cpu penalty.

Example

Currently execution is slowed from the moment Gtk is launched

julia> @time Threads.@threads for _ in 1:Threads.nthreads()
       sleep(0.1)
       end
  0.124272 seconds (43.64 k allocations: 2.401 MiB, 19.35% compilation time)

julia> @time using ProfileView
Gtk-Message: 00:04:43.548: Failed to load module "canberra-gtk-module"
Gtk-Message: 00:04:43.549: Failed to load module "canberra-gtk-module"
  2.335983 seconds (6.44 M allocations: 363.227 MiB, 4.19% gc time, 54.24% compilation time)

julia> @time Threads.@threads for _ in 1:Threads.nthreads()
       sleep(0.1)
       end
  5.116726 seconds (15.36 k allocations: 839.915 KiB, 0.20% compilation time)

julia> @time @profview sleep(1);
  2.141247 seconds (3.60 M allocations: 195.752 MiB, 2.05% gc time, 51.75% compilation time)

julia> @time Threads.@threads for _ in 1:Threads.nthreads()
       sleep(0.1)
       end
  5.115667 seconds (15.36 k allocations: 839.884 KiB, 0.18% compilation time)

## Gtk window closed here ##

julia> @time Threads.@threads for _ in 1:Threads.nthreads()
       sleep(0.1)
       end
  5.114526 seconds (15.36 k allocations: 839.931 KiB, 0.15% compilation time)

With this PR, execution is only slowed when the Gtk window is open and static

julia> @time Threads.@threads for _ in 1:Threads.nthreads()
       sleep(0.1)
       end
  0.123857 seconds (43.64 k allocations: 2.401 MiB, 18.54% compilation time)

julia> @time using ProfileView
Gtk-Message: 00:01:48.147: Failed to load module "canberra-gtk-module"
Gtk-Message: 00:01:48.147: Failed to load module "canberra-gtk-module"
  2.347176 seconds (6.44 M allocations: 362.962 MiB, 6.95% gc time, 52.47% compilation time)

julia> @time Threads.@threads for _ in 1:Threads.nthreads()
       sleep(0.1)
       end
  0.113058 seconds (15.35 k allocations: 839.790 KiB, 10.38% compilation time)

julia> @time @profview sleep(1);
  2.278810 seconds (3.60 M allocations: 196.749 MiB, 1.81% gc time, 49.04% compilation time)

julia> @time Threads.@threads for _ in 1:Threads.nthreads()
       sleep(0.1)
       end
  5.116526 seconds (15.36 k allocations: 839.884 KiB, 0.18% compilation time)

## Gtk window closed here. ##

julia> @time Threads.@threads for _ in 1:Threads.nthreads()
       sleep(0.1)
       end
  0.110775 seconds (15.35 k allocations: 839.228 KiB, 8.27% compilation time)

julia> @time Threads.@threads for _ in 1:Threads.nthreads()
       sleep(0.1)
       end
  0.110090 seconds (15.35 k allocations: 839.790 KiB, 8.71% compilation time)

julia> @time Threads.@threads for _ in 1:Threads.nthreads()
       sleep(0.1)
       end
  0.109473 seconds (15.35 k allocations: 839.790 KiB, 8.32% compilation time)

Notes

  1. Note that the blocking still happens while windows are open and static, so this is just a bandaid.

@codecov
Copy link

codecov bot commented Jan 12, 2022

Codecov Report

Merging #607 (3f464ec) into master (ceeb0b1) will decrease coverage by 0.99%.
The diff coverage is 96.42%.

❗ Current head 3f464ec differs from pull request most recent head 2e6d48d. Consider uploading reports for the commit 2e6d48d to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##           master     #607      +/-   ##
==========================================
- Coverage   56.80%   55.80%   -1.00%     
==========================================
  Files          32       32              
  Lines        2669     2238     -431     
==========================================
- Hits         1516     1249     -267     
+ Misses       1153      989     -164     
Impacted Files Coverage Δ
src/GLib/GLib.jl 75.00% <ø> (-2.28%) ⬇️
src/windows.jl 25.00% <50.00%> (+2.77%) ⬆️
src/GLib/signals.jl 75.35% <100.00%> (+0.35%) ⬆️
src/Gtk.jl 93.54% <100.00%> (+8.36%) ⬆️
src/base.jl 80.00% <100.00%> (+5.00%) ⬆️
src/GLib/gerror.jl 33.33% <0.00%> (-16.67%) ⬇️
src/theme.jl 35.71% <0.00%> (-9.29%) ⬇️
src/GLib/glist.jl 77.93% <0.00%> (-6.43%) ⬇️
src/layout.jl 71.42% <0.00%> (-6.00%) ⬇️
src/container.jl 76.47% <0.00%> (-4.93%) ⬇️
... and 24 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ceeb0b1...2e6d48d. Read the comment docs.

@IanButterworth IanButterworth changed the title Start with an idle event loop by default. Exit idle when showing anything Automatic event loop pausing when not showing anything Jan 12, 2022
@IanButterworth IanButterworth marked this pull request as draft January 12, 2022 06:54
@IanButterworth IanButterworth changed the title Automatic event loop pausing when not showing anything WIP: Automatic event loop pausing when not showing anything Jan 12, 2022
@tknopp
Copy link
Collaborator

tknopp commented Jan 12, 2022

Thanks Ian for pushing this forward, this is much appreciated!!!

I would like to ping @jpsamaroo @tkf @vtjnash @JeffBezanson @chriselrod: Can anybody from you give a perspective on this PR or what the route forward for proper Gtk mainloop integration with Julia Threads/Tasks is? It is really a problem that one cannot profile multi-threading applications with ProfileView.

@tknopp
Copy link
Collaborator

tknopp commented Jan 12, 2022

@IanButterworth: Ping me once you think this is ready and can be merged + release. A workaround a certainly better than nothing right now.

@IanButterworth IanButterworth changed the title WIP: Automatic event loop pausing when not showing anything Automatic event loop pausing when not showing anything Jan 13, 2022
@IanButterworth IanButterworth changed the title Automatic event loop pausing when not showing anything Automatic event loop start/stop when not showing anything Jan 13, 2022
@IanButterworth IanButterworth marked this pull request as ready for review January 13, 2022 02:44
@IanButterworth
Copy link
Collaborator Author

IanButterworth commented Jan 13, 2022

I'm happy with this now. Tests pass locally. Windows open and behave correctly, including with ProfileView, with no downstream code changes.

@tknopp one question is whether there are any more calls to display widgets/windows that need the handle_auto_idle(w) function added, to ensure the eventloop is started?

@jpsamaroo
Copy link
Contributor

@tknopp PR JuliaLang/julia#42302 implements threadpools, which would allow Gtk.jl to request that some subset of its tasks get scheduled in an isolated threadpool. Those tasks would be isolated from the rest of the Julia runtime (and thus achieve lower latency even under higher compute load from unrelated code). If no additional threadpools are configured, the tasks will instead remain in the primary threadpool.

I'm implementing the Julia API for threadpools now, and will let you know once the PR is ready for testing!

@IanButterworth
Copy link
Collaborator Author

Perhaps even when the threadpools work is released in base, this PR might remain a good idea?

Someone who just wants to profile their multithreaded system with Gtk probably won't want Gtk to swallow a thread while the profile is being collected?

@StefanMathis
Copy link

I do think this PR is a good idea even with thread pools (which I also think are a great idea). When I don't have an open Gtk window, I definitely want all cores to contribute as much as possible to e.g. a multithreaded loop. This means that the Gtk main loop shouldn't be active at all unless it is actually needed, which is guaranteed by this PR.

@IanButterworth
Copy link
Collaborator Author

IanButterworth commented Jan 14, 2022

I did find what appears to be a redundant show in ImageView.jl that made this PR think something was open after all windows had been closed though. Not quite sure what's happening there JuliaImages/ImageView.jl#259

But I don't think that needs to hold this back. There may be tweak PRs following this to catch all show entry and exit points.

I wonder whether the Gtk show methods return whether they actually caused anything to appear.. that way the handler could be conditionally set.

@IanButterworth
Copy link
Collaborator Author

Ok. Fixed so that things like JuliaImages/ImageView.jl#259 aren't needed. Putting it behind the :realize signal means the event loop handler is only called if the widget is actually going to be drawn (realized)

@tknopp
Copy link
Collaborator

tknopp commented Jan 14, 2022

So I am for merging this even if thread pools come in the future, which will not before 1.8 anyway. @IanButterworth: Is this good to go from your side?

@IanButterworth
Copy link
Collaborator Author

Indeed. Good to go as a non-breaking change, and I'll watch out for downstream bugs.

@tknopp tknopp merged commit c1066fb into JuliaGraphics:master Jan 14, 2022
@IanButterworth IanButterworth deleted the ib/idle_start branch January 14, 2022 17:11
@timholy
Copy link
Member

timholy commented Jan 14, 2022

This is a great idea, thanks so much for fixing this vexing problem!

Vexatos pushed a commit to CelestialCartographers/Ahorn that referenced this pull request Jan 15, 2022
Fixed buttons during initial startup not doing anything. Issue introduced by JuliaGraphics/Gtk.jl#607.
Added color names to room window color field tooltip.
Fixed left clicking with circle tool not working.
IanButterworth referenced this pull request Jan 17, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants