Bug 410293 - (C)TabFolder with many (invisible) controls slows down all redraws in that window, unlike NSTabView
Summary: (C)TabFolder with many (invisible) controls slows down all redraws in that wi...
Status: CLOSED WONTFIX
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 3.8.2   Edit
Hardware: Macintosh Mac OS X
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Platform-SWT-Inbox CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords: performance
Depends on:
Blocks:
 
Reported: 2013-06-09 18:42 EDT by Markus Persson CLA
Modified: 2020-08-21 13:05 EDT (History)
2 users (show)

See Also:


Attachments
Tabby, a snippet to show performance issues with TabFolder (4.80 KB, text/plain)
2013-06-09 18:42 EDT, Markus Persson CLA
no flags Details
TabbyView.m, a snippet to show good performance with NSTabView (3.97 KB, text/plain)
2013-06-09 19:01 EDT, Markus Persson CLA
no flags Details
NSView_tree.d, DTrace script to measure performance (1.12 KB, text/plain)
2013-06-09 19:23 EDT, Markus Persson CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Markus Persson CLA 2013-06-09 18:42:16 EDT
Created attachment 232146 [details]
Tabby, a snippet to show performance issues with TabFolder

In a window, create a TabFolder with many tabs and lots of controls for each tab. Nest TabFolders if you like, so that the total number of controls exceed 1000, but that much fewer are ever shown at the same time.

Now add a small control outside of the folders, animated by invoking refresh() on it every frame or so.

Measure the CPU load or the time required for each display refresh of that window. Watch this as you click through all tabs, exposing each control at least once. Go back to the first tab (set of tabs). Now the CPU load/time is much higher.

Repeat this using CTabFolder. Same pattern.

Repeat this in native Cocoa using NSTabView. The CPU load/time doesn't increase at all.

(We have a real RCP application that feels very sluggish on OS X due to this, in particular when anything is animated. Sure, we could dynamically create/dispose controls, but I'm not sure if all the GUI state can be maintained.)

The reason seems to be that Cocoa has never been sufficiently optimized to ignore hidden views. (Views couldn't be hidden until OS X 10.3, so other mechanisms are more used.) In particular, NSTabView removes the views of deselected tabs from the view hierarchy, only keeping the view of the selected tab in there.

(Cocoa documentation, although at least a few years old, says you shouldn't use much more than a hundred NSView:s. https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaViewsGuide/Optimizing/Optimizing.html)

Since TabFolder uses a NSTabView, it benefits from this as well, except that it puts back any view that NSTabView has detached from the view hierarchy. I presume this is because SWT relies on the Java Control hierarchy and the native NSView hierarchy staying in sync. In particular, SWT doesn't support orphaning Controls, only reparenting them.

To simply stop putting the views back works for trivial cases, like empty Composites. But it is very fragile since there are many assumptions in SWT, like that an NSView must be in some NSWindow. So I'm not sure that is a viable path. 

However, it seems to me that initially, all the views were in the view hierarchy, but they still didn't add to the execution time until they had been shown, removed by NSTabView and added back by NSTabView. I haven't yet been able to figure out in what way this differs, and if there is any way to get individual tabs/views back into the initial state.

I will attach snippets of the above using TabFolder and NSTabView, as well as a small DTrace script to measure refresh execution time and counting sent messages.

Note that I have tested this in SWT 3.8.2 and the R3_8_maintenance branch, but the same code seems to be in MASTER. Also, I tested using JDK 7 (thus only OS X 10.7 and 10.8).
Comment 1 Markus Persson CLA 2013-06-09 19:01:22 EDT
Created attachment 232147 [details]
TabbyView.m, a snippet to show good performance with NSTabView

Objective-C source code for a NSView that creates nested NSTabView:s similar to the SWT snippet. (Sorry about the code quality.)

To use it, follow these steps:
* Create a new "Cocoa Application" project using the template in Xcode (not document based, but using ARC).
* Create a new File -> Cocoa -> Objective-C class, called TabbyView and being a subclass of NSView. Replace the TabbyView.m with my version.
* Open MainMenu.xib, click in the middle of the main window. Activate the Identity Inspector (Option-Cmd-3) and at the "Custom Class" at the top change "NSView" to "TabbyView".
* Run (Cmd-R).
Comment 2 Markus Persson CLA 2013-06-09 19:23:18 EDT
Created attachment 232148 [details]
NSView_tree.d, DTrace script to measure performance

This DTrace script can be run using

sudo dtrace -s NSView_tree.d -p<PID>

where <PID> is the process id of either the TabFolder snippet or the NSTabView snippet.

At most once a second, the script measures a few things during the first encountered invocation of [NSView displayIfNeeded], and displays the result:
* Estimation of the number of NSViews visited (invocations of some internal method).
* The number of invocations of [NSView isOpaque] and their return values.
* The number of invocations of [NSView isHiddenOrHasHiddenAncestor] and their return values. (When running my snippets, [NSView isHidden] is never invoked, so I first thought visibility wasn't checked at all.)
* Total execution time in microseconds.
Comment 3 Markus Persson CLA 2013-06-09 19:32:19 EDT
Some info regarding my snippets. Besides the nested tab folders (totally 1110 tabs), there are two buttons. One that cycles through all the tabs at all levels until each folder is back at the first tab. And one that can be toggled to animate a horizontal line across (or below in the Cocoa version) the button itself.
Comment 4 Eclipse Genie CLA 2020-08-21 13:05:42 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. As such, we're closing this bug.

If you have further information on the current state of the bug, please add it and reopen this bug. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.