Bug 161569 - SWT Splash Screen prototype
Summary: SWT Splash Screen prototype
Status: RESOLVED FIXED
Alias: None
Product: Equinox
Classification: Eclipse Project
Component: Framework (show other bugs)
Version: 3.3   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: equinox.framework-inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 109290 154088
  Show dependency tree
 
Reported: 2006-10-19 10:14 EDT by Andrew Niefer CLA
Modified: 2007-02-12 15:07 EST (History)
11 users (show)

See Also:


Attachments
Prototype containing both methods (183.38 KB, application/octet-stream)
2006-10-19 10:21 EDT, Andrew Niefer CLA
no flags Details
Patch to osgi for method 3 (20.67 KB, patch)
2006-10-25 15:11 EDT, Andrew Niefer CLA
no flags Details | Diff
New Method #4 (330.57 KB, application/octet-stream)
2006-10-31 16:57 EST, Andrew Niefer CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Andrew Niefer CLA 2006-10-19 10:14:38 EDT
Moving from email thread:  See also http://wiki.eclipse.org/index.php/Splash_Screen_Improvements

Here is a summary of the results of my splash screen explorations.  For comparison purposes, I'm getting the native splash screen showing up around 425 ms after the launcher is executed.

I tried 2 methods of bringing up the splash screen:
1) "Bundle" Splash Screen:  1.3 seconds after launcher starts.
 A splash bundle, depending on swt, is placed on the osgi.bundles list with start level 1.  The EclipseStarter was modified to start the framework on a separate thread and display the splash screen as soon as the splash bundle is activated.
 Note that the bundle starts before update.configurator, so even on a clean install, we are still showing the splash before most of the heavy lifting starts.
Notes:
Too slow
Requires changes to EclipseStarter.
Involves 1 new bundle

2) "Main" Splash Screen: 535 ms after launcher starts (110 ms slower than native splash).
In the startup.jar, Main creates a custom classloader containing swt & the splash bundle.  An adaptor hook is used to create a DefaultClassLoader which delegates to the splash classloader for swt.  Splash screen is displayed then the framework started.  I did not start any new threads, the splash screen is not running an event loop.   Adapator hooks can be used to update the splash screen periodically as the framework loads.

Notes:
-system libraries are extracted to (for example): configuration\org.eclipse.osgi\bundles\org.eclipse.swt.win32.win32.x86_3.3.0.v3309.jar\
-Main should only display the splash if it is certain that the adaptor hook is present.  SWT breaks if we don't manage to use the same classloader for the splash and the swt bundle.
-What happens if more than one version of the swt fragment is present.
-Prototype does not yet handle security.
-Involves 2 new bundles (currently org.eclipse.platform.splash for adaptor hooks and org.eclipse.splash for displaying splash)


We need to decide if we are doing this or not so that Kim can plan the workbench side of things.  I think we should go with method 2 and keep the native splash screen as a backup if main decides it can't find the adaptor hook or can't decide on which fragment to use.

Attached are patches and bundles for the prototypes.  Start with "-nosplash -vmargs  -Dorg.eclipse.splash.bundle" for method 1, and  "-vmargs  -Dorg.eclipse.splash.main" to use method 2.  Note the osgi.bundles and osgi.framework.extensions properties in the included config.ini.
Comment 1 Andrew Niefer CLA 2006-10-19 10:18:35 EDT
Pascal's Reply: 
I just thought about a variation around 2: once we have the classloader of the framework, could not we use reflection to create an instance of the DefaultClassLoader to back swt?
Actually could the classloader be usable? Tom?

Tom:
Jeff and I discussed this approach at JAOO.  I cannot remember all the issues, but in the end it will take quite a bit of restructuring to make the BundleClassLoader work without the rest of the framework around.  Also none of the adaptor hooks will be loaded so they cannot participate in the class loading of SWT.  To be honest this type of change seems pretty scary to me.  It seems like so many things could go wrong by loading SWT from a classloader that is not a BundleClassLoader.

Pascal:

Note that if we go with solution 2 like recommended by Andrew none of the hooks or adaptors will be used for the SWT anyway since it is completly loaded by another classloader created in main.
Which change do you refer to? The change in BundleClassLoader or the change in the splash screen in general?

If we go with solution 2, we also have to decide if is ok to have SWT loaded in a special way.
Comment 2 Andrew Niefer CLA 2006-10-19 10:21:24 EDT
Created attachment 52316 [details]
Prototype containing both methods
Comment 3 Alex Blewitt CLA 2006-10-19 13:22:54 EDT
I think this would be a great thing to have, rather than the manual startup process at the moment. I think that option 1) is cleaner, and easier to understand what's going on ... but I take the point that it's a bit slow. Having said that, on a reasonable spec PC, it takes a couple of seconds for the splash screen to be displayed on my PC anyway, so I don't think 1.3s is that bad. 

(Just my 2c)
Comment 4 Andrew Niefer CLA 2006-10-19 14:33:12 EDT
Just to clarify, the adaptor hook in method 2, creates a SplashClassLoader which extends DefaultClassLoader.  The only method it overrides is findLocalClass where it delegates to the earlier created swt classloader.  It could likely be made smarter by paying attention to DefaultClassLoader.attachFragment.  It could also consult the hook registry is we wanted.
Comment 5 Andrew Niefer CLA 2006-10-19 15:56:24 EDT
After talking with Pascal, we have a couple of ideas that might speed up method 1.  I will try them and put the results here.
Comment 6 Andrew Niefer CLA 2006-10-20 13:38:01 EDT
I was able to get the time to splash for method 1 down to around 1100 ms on a clean install and 875ms when a state cache exists.

This was done by not spawning a separate thread for the framework and not spinning an event loop for the splash screen (as well as using buffered I/O).  The idea was that we could use an adaptor hook to update the splash screen periodically as the framework starter.

The problem is, the adaptor hooks seem to be all called on daemon threads.  This won't work to update the splash (the display needs to be updated from the main ui thread).  This means that without hooks that are called on the main thread, neither the improved method 1 nor method 2 will work without spawning threads.

Tom, is there any way to get hooks called on the main thread where they can be used to update the splash?
Comment 7 Thomas Watson CLA 2006-10-20 14:55:39 EDT
Which hook interface are you referring to?  In general the hooks have no control over what thread they run on.  In every case they are being called synchronously from the same thread where the work is being done.
Comment 8 Pascal Rapicault CLA 2006-10-20 21:16:00 EDT
Maybe could we get a special API from SWT...
Comment 9 Jeff McAffer CLA 2006-10-20 21:47:16 EDT
Andrew and I talked about this and the threading/hook thing is not the issue.  Bascially the hooks get called on whatever thread they are called on.  The approach was to use a hook to drive the UI and keep it live but since hooks are called on whatever thread, this would not work.

Another approach was discovered though not yet tested.  Bascially while the start level is going up (and all the update.configurator in particular is being started), the main thread is waiting on a semaphore.  This semaphore will eventualy be kicked by a listener listening for start level changed events where the new start level is the final start level (6 in the default case).  So main is stuck in Semaphore.acquire().

Instead, the proposal is to have an "Animator" object that is called instead of acquire.  The animator would be given a context taht it should poll to see if it should return.  the listener that used ot signal the semaphore would instead tweak the context to say it was done.

The animator could then do whatever it wanted as long as it promised to check for "doneness" frequently.  As such, it could watch for paint events, watch the Framwork events and report, for example, which bundles are being installed/started, ...

note also that the same animator might be called or used later on while the application is being run.

StartupMonitor might be a better, more generic term for Animator...
Comment 10 Jeff McAffer CLA 2006-10-20 21:55:56 EDT
we also talked about another approach to fast startup.  

#1 seemed to startup all of OSGi (load all the state etc) and then have a set of bundles that start early (start level 1) to do the animation.  

#2 by contrast tried to start the animation/splash even before the framework/state were loaded

1 is simple but does not scale well since the state may be large and take a long time to load on cold starts.  2 is scales well but requires significant magic to get the classloaders right and enable the various hooks to take effect.  These approaches represent opposite ends of a spectrum.

We want to do 1 but must address the scaling problem.  Interestinly, the code that loads the state *appears* to just read the state file sequentially and add the bundles to an in-memory bundle repository.  Furthermore, the first bundles read are *likely* ot be the ones listed on the osgi.bundles list.  So the proposal is to modify EclipseStarter.startup() as follows

- start the framework reading only the first N bundles (where N is the size of the osgi.bundles list) from the state file (see getInstalledBundles())
- this bundle list would include SWT, SWT fragment and the splash bundle
- start the splash (paints it)
- read the rest of the bundles
- bump up the start level and animate the splash (see previous comment)
- yadda yadda

Andrew is going to prototype and see what happens.  The key question is whether or not we can incrementally load the state like that and can we ensure that the "early start" bundles are first in the state file.

Comment 11 Pascal Rapicault CLA 2006-10-22 20:02:06 EDT
The approach proposed in the previous comment seems to be related to bug #106169.

Andrew, are the times available in comment #6 measured using JNI launching? Is there any gain using JNI launching?
Comment 12 Jeff McAffer CLA 2006-10-22 20:24:59 EDT
Yup though bug 106169 would likely have deeper impacts.  Essentially the second read would potentially be from a different state file.  Not sure how that would work out for the lazy loading of wiring information etc.  But definitely related.
Comment 13 Andrew Niefer CLA 2006-10-23 10:52:04 EDT
I've started on the prototype Jeff mentioned, just a couple of comments:
1) The N bundles actually seems to be osgi.framework.extensions + osgi.bundles
2) I'm having problems getting things to resolve after adding the remaining bundles, I still have to figure out what is missing here.

RE: Comment #11, No those times do not include JNI launching.  I would like to think that JNI launching would save us some time, but I have no measurements for this.  I will try to include some JNI measurements when I get times for this new prototype.
Comment 14 Pascal Rapicault CLA 2006-10-23 11:45:00 EDT
Before you spend some non trivial time prototyping Jeff's proposition about progressive loading, I would recommend that you first measure if this has a potential for speed improvement by just running a minimal set of bundles and doing the measurements.
Comment 15 Jeff McAffer CLA 2006-10-23 21:10:31 EDT
Note that some aspects of the suggested path will not work without changes to the way that the state is actually stored.  Some brief discussions with Tom today shed some light on the issues but it would be better for Tom to comment with actual facts rather then me with speculation...

As a reminder for Tom, there was some discussion around just loading the bundle datas (perhaps just the ones needed) and constructing real classloaders for them (but not bundle loaders) and linking them togehter through a simple switching delegating parent classloader.  That way all the "boot" bundles would be loaded and run without having the state and with only the minimal bundle data's loaded.

As for timing, with profiling on the interesting times are 
   OSGi created
   osgi launched
These are the ones that do the hard work of loading the state etc. and would need to scale. 

Comment 16 Andrew Niefer CLA 2006-10-25 15:11:28 EDT
Created attachment 52695 [details]
Patch to osgi for method 3

Attached is a patch for "method 3".  As in Jeff's suggestion:
1) Read N "boot" bundles from the bundledata
2) Create BundleClassLoaders for these N bundles
3) Display Splash
4) Load the rest of the bundles
5) continue as normal: launch the framework, read the state, etc

On a warm machine, reading only boot bundles before the splash saves approximately 300 ms on my target which contains 1067 plugins.  Creating the BundleClassLoaders and displaying the splash before the state is read saves an additional 200 ms or so.  This gives us a splash screen approximately 800 ms after launch.

On a "cold" start (using clearmem.exe), the native splash screen appears about 7 seconds after launch.  The swt splash appears at around 11 seconds. (In both of these cases, it is about 6.5s to get to Main).

- We will want to define a new property osgi.boot.bundles instead of using osgi.bundles
- We need to define a way to specify what to run out of the boot bundles.  This gets run before the framework is launched.
- We need to ensure that the state is written in the expected order.  Currently we assume that the boot bundles come first, and that any fragment boot bundles come immediately after their host.
Comment 17 Andrew Niefer CLA 2006-10-25 16:38:43 EDT
A pretty table to compare times.  Funky Classloader is method 2, aka "Main" splash.  Boot Bundles is method 3.  Times are milliseconds from the time recorded at the start of main() in the native launcher.  Time to Main is the time in the java method Main.handleSplash, right before we would normally launcher the process for the native splash screen.

Cold Start:
		Time to Main 	Time to Splash  Difference
Native Splash	   6398		    6821	   423
Funky Classloader  6469		    7781	   1312
Boot Bundles	   6988		    11474	   4487

Warm Start:
Native		   253		    401		   146
Funky Classloader  263		    589		   325
Boot Bundles	   255		    791		   536
Comment 18 Jeff McAffer CLA 2006-10-25 20:42:06 EDT
There is a futher option that we might investigate... With the help of SWT we could look at trying to allocate the windowsytem's display handle in C in the launcher and eventually pass it along to SWT and have it wrapped by a Display object.  This way we could have the exe show an initial splash perhaps even sooner than the current one and hand off the created display handle to Eclipse/Java (since we would be in the same process) for further animation.  If we do that then the we don't have to do so much frigging around with the classloaders etc.  I have no idea if this is feasible.
Comment 19 Ismael Juma CLA 2006-10-25 20:53:27 EDT
Out of curiosity, did anyone look at the approach taken by Sun with JDK6[1]? I haven't looked into it in detail, but it seems like it's something similar to comment #18 (but using AWT instead of SWT) since the Splash Screen is first displayed before the VM starts.

[1] http://java.sun.com/developer/technicalArticles/J2SE/Desktop/javase6/splashscreen/
Comment 20 Pascal Rapicault CLA 2006-10-25 21:07:33 EDT
Ismael is correct, this is the approach taken by Java 6. However a while ago, I talked with Steve and Silenio about it, but I seem to recall that there were some problems that I don't precisely remember about (probably about spinning the event loop) but I still think that we should explore again.
Comment 21 Andrew Niefer CLA 2006-10-31 16:57:18 EST
Created attachment 53029 [details]
New Method #4

The new and improved magic SWT splash screen! (method #4, comments 18, 19)

1) Native launcher displays splash screen
2) VM is started using JNI, handle to splash screen is passed into java
3) Splash bundle wraps native window with a shell and does swt stuff in the splash

Warm start Time to Splash: 50ms
Cold start Time to Splash: 400ms

Use the included patch to export new versions of ui.workbench and swt.win32.win32.x86.  Then extract the zip over a fresh eclipse and delete the old workbench and swt fragment.  Start with 
eclipse -showsplash 600 splash.bmp -clean -vm C:\Java\SUN_1.5.0_07\jre\bin\client\jvm.dll -vmargs -Dorg.eclipse.splash.bundle
(pointing to your own jvm.dll)

This method requires:
1) A JNI launcher (bug 82518)
2) Changes in SWT to wrap a shell around an existing natively created window
3) Changes in Main and EclipseStarter to paint the splash screen during startup
Comment 22 Andrew Niefer CLA 2007-02-12 15:07:29 EST
Method 4 is being used in 3.3M5