#summary Design document and user manual for code splitting =Introduction = As AJAX apps develop, the !JavaScript part of the application tends to do more and more work. At some point, the code itself often becomes large enough that it has a significant impact on the application's startup time. To help with this issue, GWT provides Dead-for-now (DFN) code splitting. This article talks about what DFN code splitting is, how you start using it in an application, and how to improve an application that does use it. =Prerequisites= You need a copy of GWT compiled from trunk. Code splitting is not available in the released versions of GWT as of version 1.6. =Limitations= Code splitting is only supported with certain linkers. Specifically, the iframe linker is supported, but the cross-site linker is not yet. The -soyc flag is currently only available on the GWTCompiler entry point, not the new Compiler entry point. =How to use it= To get your code split, simply insert calls to GWT.runAsync() at the places where you want the program to be able to pause for downloading more code. These locations are called _split points_. A call to GWT.runAsync() is just like a call to register any other event handler. The only difference is that the event being handled is somewhat unusual. Instead of being a mouse-click event or key-press event, the event is that the necessary code has downloaded for execution to proceed. For example, here is the initial, unsplit Hello sample that comes with GWT: {{{ public class Hello implements EntryPoint { public void onModuleLoad() { Button b = new Button("Click me", new ClickHandler() { public void onClick(ClickEvent event) { Window.alert("Hello, AJAX"); } }); RootPanel.get().add(b); } } }}} Suppose you wanted to split out the Window.alert call into a separate code download. The following code accomplishes this: {{{ public class Hello implements EntryPoint { public void onModuleLoad() { Button b = new Button("Click me", new ClickHandler() { public void onClick(ClickEvent event) { GWT.runAsync(new RunAsyncCallback() { public void onFailure(Throwable caught) { Window.alert("Code download failed"); } public void onSuccess() { Window.alert("Hello, AJAX"); } }); } }); RootPanel.get().add(b); } } }}} In the place the code used to call Window.alert, there is now a call to GWT.runAsync. The argument to GWT.runAsync is a callback object that will be invoked once the necessary code downloads. Like with event handlers for GUI events, a runAsync callback is frequently an anonymous inner class. That class must implement !RunAsyncCallback, an interface declaring two methods. The first method is onFailure(), which is called if any code fails to download. The second method is onSuccess(), which is called when the code successfully arrives. In this case, the onSuccess() method includes the call to Window.alert(). With this modified version, the code initially downloaded does not include the string "Hello, AJAX" nor any code necessary to implement Window.alert. Once the button is clicked, the call to GWT.runAsync will be reached, and that code will start downloading. Assuming it downloads successfully, the onSuccess() method will be called; since the necessary code has downloaded, that call will succeed. If there is a failure to download the code, then onFailure() will be invoked. To see the difference in compilation, try compiling both versions and inspecting the output. The first version will generate cache.html files that all include the string "Hello, AJAX". Thus, when the app starts up, this string will be downloaded immediately. The second version, however, will not include this string in the cache.html files. Instead, this string will be located in cache.js files underneath the deferredjs directory. In the second version, the string is not loaded until the call to runAsync is reached. This one string is not a big deal for code size. In fact, the overhead of the runAsync run-time support could overwhelm the savings. However, you aren't limited to splitting out individual string literals. You can put arbitary code behind a runAsync split point, potentially leading to very large improvements in your application's initial download size. =Code-splitting development tools= You've now seen the basic code-splitting mechanism that GWT provides. Unfortunately, when you first try it, your program is likely not to split up exactly like you hoped. You will try to split out some major subsystem, but there will be a stray reference to that subsystem somewhere reachable without going through a split point, and so much of the subsystem will get pulled into the initial download. For that reason, effective code splitting requires iteration. You have to try one way, look at how it worked, then make modifications to get it working better. This section describes several tools that GWT provides for iterating toward better code splitting. ==The results of code splitting== Before going further, it is important to understand exactly what fragments the code splitter divides your code into. That way you can examine how the splitting went and work towards improving it. One very important fragment is the initial download. For the iframe linker it is emitted as a file whose name ends with cache.html. When the application starts up, the initial-download fragment is loaded. This fragment includes all the code necessary to run the application up to any split point but not past. When you start improving your code splitting, you should probably start by trying to reduce the size of the initial download fragment. Reducing this fragment causes the application to start up quickly. There are a number of other code fragments generated in addition to this initial one. For the iframe linker, they are located underneath a directory named deferredjs. Each split point in the program will have an associated code fragment. In addition, there is a "leftovers" code fragment for code that is not associated with any specific split point. The code fragment associated with a split point is of one of two kinds. Most frequently, it is an "exclusive" fragment. An exclusive fragment contains code that is only needed once that split point is activated. If the split point is an "initial" split point, then it gets an "initial" code fragment rather than an "exclusive" one. Unlike an exclusive fragment, an initial fragment does not rely on anything in the leftovers fragment. However, an initial fragment can only be loaded in its designated initial load sequence; exclusive fragments have the benefit that they can be loaded in any order. ==The Story of Your Compile (SOYC)== Now that you know how GWT splits up code in general, you will want to know how it splits up your code in particular. There are several tools for this, and they are all included in the Story of Your Compile (SOYC). To obtain a SOYC report for your application, there are two steps necessary. First, add -soyc to the compilation options that are passed to the GWT compiler. This will cause the compiler to emit raw information about the compile to XML files in an -aux directory beside the rest of the compiled output. In that directory, you will see an XML file for each permutation and a manifest.xml file that describes the contents of all the others. The second step is to convert that raw information into viewable HTML. This is done with the !SoycDashboard tool. To build the tool, type "ant tools" at the top of your GWT checkout. Then, run it by running Java with the following settings: * JVM argument `-Xmx1024m` (higher if you need) * classpath `build/lib/gwt-soyc-vis.jar:build/lib/gwt-dev-linux.jar` * main class `com.google.gwt.soyc.SoycDashboard` * a command-line argument of "-resource build/lib/gwt-soyc-vis.jar" * a command-line arguments of "stories0.xml.gz", "dependencies0.xml.gz", "splitPoints0.xml.gz" * a working directory in the same location as manifest.xml You can optionally specify a `-out` flag specifying where the output should go; by default the output is into the current directory. The top-level HTML page to open is `SoycDashboard-index.html`. ==Overall sizes== The first thing to look at in a SOYC report is the overall size breakdown of your application. SOYC breaks down your application size in four different ways: by Java package, by code type, by type of literals (for code associated with literals), and by type of strings (for code associated with string literals). By looking at these overall sizes, you can learn what parts of the code are worth much effort to pay attention to when splitting. For that matter, you might well see something that is larger than it should be; in that case, you might be able to work on that part and shrink the total, pre-split size of the application. ==Fragment breakdown== Since you are working on code splitting, you will next want to look at the way the application splits up. Click on any code subset to see a size breakdown of the code in that fragment. The "total program" option describes all of the code in the program. The other options all correspond to individual code fragments. ==Dependencies== At some point you will try to get something moved out of the initial download fragment, but the GWT compiler will put it there anyway. Sometimes you can quickly figure out why, but other times it will not be obvious at all. The way to find out is to look through the dependencies that are reported in the SOYC report. The most common example is that you expected something to be left out of the initial download, but it was not. To find out why, browse to that item via the "initial download" code subset. Once you click on the item, you can look at a chain of dependencies leading back to the application's main entry point. This is the chain of dependencies that causes GWT to think the item must be in the initial download. Try to rearrange the code to break one of the links in that chain. A less common example is that you expected an item to be exclusive to some split point, but actually it's only included in leftover fragments. In this case, browse to the item via the "total program" code subset. You will then get a page describing where the code of that item ended up. If the item is not exclusive to any split point, then you will be shown a list of all split points. If you click on any of them, you will be shown a dependency chain for the item that does not include the split point you selected. To get the item exclusive to some split point, choose a split point, click on it, and then break a link in the dependency chain that comes up. =Specifying an initial load sequence= By default, every split point is given an exclusive fragment rather than an initial fragment. This gives your application maximum flexibility in the order the split points are reached. However, it means that the first split point reached must pay a significant delay, because it must wait for the leftovers fragment to load before its own code can load. If you know which split point in your app will come first, you can improve the app's performance by specifying an initial load sequence. Simply add a line such as the following to your module's gwt.xml file: {{{ }}} The `value` part of the line specifies a split point. Currently the only way to specify a split point is to include a complete JSNI reference to the method enclosing the split point in question. For some applications, you will know not only the first split point reached, but also the second and maybe even the third. You can continue extending the initial load sequence by adding more lines to the configuration property. For example, here is module code to specify an initial load sequence of three split points. {{{ }}} The down side to specifying an initial load sequence is that if the split points are reached in a different order than specified, then there will be an even bigger delay than before before that code is run. For example, if the third split point in the initial sequence is actually reached first, then the code for that split point will not load until the code for the first two split points finishes loading. Worse, if some non-initial split point is actually reached first, then all of the code for the entire initial load sequence, in addition to the leftovers fragment, must load before the requested split point's code can load. Thus, think very carefully before putting anything in the initial load sequence if the split points might be reached in a different order at run time. =Common coding patterns= GWT's code splitting is new, so the best idioms and patterns for using it are still in their infancy. Even so, here are a couple of coding patterns that look promising. Keep them in mind for your coding toolbox. ==Async Provider == Frequently you will think of some part of your code as its own coherent module of functionality, and you'd like for that functionality to get associated with a GWT exclusive fragment. That way, its code will not be downloaded until the first time it is needed, but once that download happens, the entire module will be available. A codding pattern that helps with this goal is to associate a class with the module and then make sure that all code in the module is only reachable by calling instance methods on that class. Then, you can arrange for the only instantiation of that class in the program to be within a runAsync. The overall pattern looks as follows. {{{ public class Module { // public APIs public doSomething() { /* ... */ } public somethingElse() { /* ... */ } // the module instance; instantiate it behind a runAsync private static Module instance = null; // A callback for using the module instance once it's loaded public interface ModuleClient { void onSuccess(Module instance); vaid onUnavailable(); } /** * Access the module's instance. The callback * runs asynchronously, once the necessary * code has downloaded. */ public static void createAsync(final ModuleClient client) { GWT.runAsync(new RunAsyncCallback() { public void onFailure(Throwable err) { client.onUnavailable(); } public void onSuccess() { if (instance == null) { instance = new Module(); } client.onSuccess(instance); } }); } } }}} Whenever you access the module from code that possibly loads before the module, go through the static Module.createAsync method. This method is then an "async provider": it provides an instance of Module, but it might take its time doing so. Usage note: for any code that definitely loads after the module, store the instance of the module somewhere for convenience. Then, access can go directly through that instance without harming the code splitting. ==Prefetching== The code splitter of GWT does not have any special support for prefetching. Except for leftovers fragments, code downloads at the moment it is first requested. Even so, you can arrange your own application to explicitly prefetch code at places you choose. If you know a time in your application that there is likely to be little network activity, you might want to arrange to prefetch code. That way, once the code is needed for real, it will be available. The way to force prefetching is simply to call a runAsync in a way that its callback doesn't actually do anything. When the application later calls that runAsync for real, its code will be available. The precise way to invoke a runAsync to have it do nothing will depend on the specific case. That said, a common general technique is to take extend the meaning of any method parameter that is already in scope around the call to runAsync. If that argument is null, then the runAsync callback exits early, doing nothing. For example, suppose you are implementing an online address book. You might have a split point just before showing information about that contact. A prefetchable way to wrap that code would be as follows: {{{ public void showContact(final String contactId) { GWT.runAsync(new RunAsyncCallback() { public void onFailure(Throwable caught) { cb.onFailure(caught); } public void onSuccess() { if (contactId == null) { // do nothing: just a prefetch return; } // Show contact contactId... } }); } }}} Here, if showContact() is called with an actual contact ID, then the callback displays the information about that contact. If, however, it is called with null, then the same code will be downloaded, but the callback won't actually do anything.