GWT Extreme!
-
Upload
cromwellian -
Category
Technology
-
view
15.208 -
download
4
description
Transcript of GWT Extreme!
GWT Extreme!Performance and FlexibilityRay Cromwell, CTO, Timefire, Inc05/29/2008
BUDD: Why did he call it GWT Extreme?
BILL: I guess he thought it sounded kinda cool.
Extreme Performance and Flexibility
• “Faster (and Smaller) than Possible” code• Parsing and Evaluating CSS Selector Queries• High Performance Graphics
• Bridging GWT with other execution environments• Remote procedure calls to Android, Flash, and Gears
• Writing Flash and Gears Workers in Java• Tricks to compile subsets of your source as separate
packages• Q&A if time permits
GWT Bloated and Slow? Think Again
• Faster• Generate Java code on the fly at compile time• Compiler inlines small Java and Javascript methods• Devirtualizes most other method calls• And tons more, too many to list….
• Smaller• Prunes and eliminates anything not used• Aggressively obfuscates code
• Obfuscated identifiers chosen to maximize DEFLATE compression• Replace runtime operations with compile time
“Faster and Smaller than Possible” Code
GwtQuery (aka GQuery)
• JQuery-like API in GWT• Uses GWT 1.5 language features for succinct code• Run-time CSS3 Selector parsing and evaluation• Compile-time CSS3 Selector parsing• Leverages modern browser features when available:
• XPath (document.evaluate)• W3C CSS Selector API (document.querySelectorAll)• getElementsByClassName()
GwtQuery Example
Runtime Evaluation
• Find all “menus”, make first element text “Hello”
import gwtquery.client.GwtQuery;import static gwtquery.client.GwtQuery.$;
public class GwtQueryModule implements EntryPoint { public void onModuleLoad() { $(“ul.menu > li:first-child”).html(“hello”); }}
GwtQuery Example
What does the $(selector) function translate into?
• On Safari 3.1• document.querySelectorAll(selector)• On Firefox, Opera, older Safari• Lots of Regexp parsing to compute xpathExpression• document.evaluate(xpathExpression)• On IE < 8 and others, a bunch of Regexp and DOM calls• Note: Each browser gets separately compiled JS
Digression: Generators
• Feature of Deferred Binding Mechanism• GWT.create(RequestedType.class)• Replace requested type with generated implementation• Turns into new GeneratedType()
GwtQuery Example
Compile-time parsing
• Selectors usually literal strings, known at compile time• Why parse and translate these at runtime?!• Perform parsing and translation to XPath or JS-DOM
navigation calls at compile-time
public MySelectors extends Selectors { @Selector(“ul.menu > li:first-child”) GQuery allFirstMenuItems();}
MySelectors selector = GWT.create(MySelectors.class);
selector.allFirstMenuItems().html(“hello”);
GwtQuery Example
Compile-time parsing
• GWT.create(MySelectors.class) triggers Generator• Different Generator for each browser (DOM, XPath, etc)• Generator creates implementation of MySelectors interface• For each method with a @Selector annotation• Parses CSS Selector, emits implementation of method• Compiler inlines method, elides everything else
GwtQuery Example
Compile Time Parsing: Bottom Line
ul > li:first-child
becomes
document.evaluate(“ul/li[position() = 1]”)
GwtQuery Example
How big is the output?
• jQuery 1.2.3• ~3400 lines of Javascript• 98kb unprocessed source• 15kb obfuscated and gzipped• GwtQuery• ~3400 lines of Java• 116kb on disk• How big is it compiled?• 15kb or larger?• How about 7kb, a 50% reduction?• 3kb sounds about right!• 712 bytes is the answer!• Smaller than single packet, HTTP headers
Caveats and Objections
• Trivial example• More reasonable example would exercise larger part of API• and reduce amount of pruned code• Example shows how aggressive GWT optimizer is• past GWT critics have used Hello World examples to
demonstrate purported “bloat”
Demo: Progressively Enhancing <UL> into PowerPoint-like slide transitionsSample Code
<div class="slide transition-fade"> Slide 1 <ul class="transition-fade"> <li>Point One</li> <li>Point Two</li> <li>Point Three</li> </ul></div><div class="slide transition-fade"> Slide 2 <ul class="transition-appear"> <li>Slide 2 Point One</li> <li>Slide 2 Point Two</li> <li>Slide 3 Point Three</li> </ul></div>
GwtQuery code
Sample Code for Selectors needed
interface Slide extends Selectors { // find all LI elements rooted at ctx @Selector("li") NodeList<Element> slideBulletsCtx(Node ctx);
// Find all DIV elements with class 'slide' @Selector("div.slide") NodeList<Element> allSlides(); }
GwtQuery code
Sample Code for Event Handling
final Slide s = GWT.create(Slide.class);$(Document.get().getBody()).click(new Function() { int curSlide = 0; int curBullet = 0; GQuery slides = $(s.allSlides()); GQuery bullets = $(s.slideBulletsCtx(slides.get(curSlide)));
public boolean f(Event e) { if (curBullet < bullets.size()) bullets.eq(curBullet++).as(Effects).fadeIn(); else bullets.css("opacity","0"); slides.eq(curSlide).css("display", "none"); curSlide++; if(curSlide == slides.size()) curSlide = 0; curBullet = 0; bullets = $(s.slideBulletsCtx(slides.get(curSlide))); slides.eq(curSlide).css("display","block") .as(Effects).fadeIn(); return true; }});
Sample of Generated Output for Selector “.slide li”
W3C Selector API version
public class SlideNativeImpl extends SelectorEngine implements MySelectors { public final NodeList<Element> slideBullets() { return querySelectorAll(".slide li"); }}
Sample of Generated Output for Selector “.slide li”
XPath Version
public class SlideXPathImpl extends SelectorEngine implements Slide { public final NodeList<Element> slideBullets() { return xpathEvaluate("./descendant::*[contains(concat(' ', @class, ' '), ' slide ')]/descendant::li"); }}
GWT Query
• Alpha Released Today• Available at http://gwtquery.com• Help Contribute to Core and Plugins!
High Performance Graphics
Extreme Graphics Performance
• Immediate Mode vs Retained Mode– Canvas vs SVG/VML
• JS VMs top out at between 1000-10000 method calls on Canvas
• Can we cache/reuse work?• Take a page from OpenGL’s Playbook:
Display Lists
Extreme Graphics Performance: Display Lists
• Record a sequence of draw calls• Replay sequence like a macro• Framework/driver can perform optimizations
– Collapse sequence of scale/rotate/translate operations into setTransform call
– Cull/Clip some operations if possible– Cache result as bitmap where possible
• DEMO!
Extreme Graphics Performance and Flexibility:Chronoscope Chart Library
• 20,000 lines of code– 500kb of source– compiles to 58kb compressed
• 30,000+ points at interactive rates• Javascript, Flash, Applet, Servlet, and Android• Multi-grained API
– Full control with Java+GWT– Higher level with Javascript API– Or with no coding at all via Microformats and CSS
• DEMO!
Some insights from porting to GWT 1.5
• Method inlining yields up to 100% perf gains• Ability to inline JSNI provided huge gains for
IE/Flash version• Slight increase in size • Abstractions don’t hurt much• Beware usage of long, costs of non-double
types• Static initializers can be harmful
Chronoscope and Mobile Code with GWT
• Platform neutral code • Only 4 classes needed to run in other
environments or on the “cloud”– Flash, Android, Applet, Servlet
• GWT methods automatically exported to Javascript via GWT-Exporter library
Integrating GWT with other Environments
Integrating GWT with other environments
• GWT already integrates with Servlets• JSNI is used to integrate with Browser/JS• Extend RPC model to arbitrary containers
– Android– Flash– Gears Workers
Example: Syndroid
Gadgets can span many Platforms
Android iGoogle OSX GoogleDesktop
Yahoo
And many Flash widget startups
Example: Syndroid
• Synthesis of Android and GWT• Write Gadgets to a restricted API• Compile to Javascript or Dalvik or (later)
Actionscript• Deploy on Android, iGoogle, OSX, Yahoo,
et al• Prototype DEMO!
Example: Android Native Interface
What if Android browser had a window.locationService object?
public class LocationServiceClient implements LocationService { public native String getLocation() /*-{ return $wnd.locationService.getLocation(); }-*/;}
Generators + Linkers = Awesomeness
• Define RPC interface (sync or async)• Use Generator to generate client stub
• Params may or may not need to be serialized• Also generate “server” to handle request• “Server” handling code may need
bootstrapping• Linker used to generate bootstrap code• If needed, use Linker to package/compile
Android Native InterfaceAndroidNativeInterface
LocationService
String getLocation();
extends
LocationServiceClient
String getLocation() { return $wnd.locationService.getLocation();}
implements
Emitted by Generator
LocationServiceImpl
String getLocation() { return ...android native code...}
impl
Written by Developer
Android Bootstrap
Bind LocationServiceImpl towindow.locationService
Provided by Linker
Example: Android Native Interface
Synchronous RPC
public interface AndroidNativeInterface {}
@ANIBinding("locationService")@ImplementedBy("LocationServiceImpl")public interface LocationService extends AndroidNativeInterface { String getLocation();}
Example: Android Native Interface
Implement Android side (not legal GWT code!)
public class LocationServiceImpl implements LocationService { public String getLocation() { try { Location currentLocation = ((LocationManager) context.getSystemService(Context.LOCATION_SERVICE)) .getCurrentLocation("gps");
return "Lat: "+currentLocation.getLatitude()+ ", Long: "+currentLocation.getLongitude(); } catch (Throwable e) { return "ExceptionInNativeAndroid:"+e.getClass().getName()+": "+e.getMessage(); } }}
Example: Android Native Interface
To use
LocationService service = GWT.create(LocationService.class);Window.alert(“Your location is: “+service.getLocation());
Example: Linker Generated BootstrapActivity
LocationServiceImpl gets exposed as window.locationService
public class BootstrapActivity extends Activity { @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(getBootstrapView()); } public WebView getBootstrapView() { WebView wv=new WebView(this); wv.getSettings().setJavaScriptEnabled(true); wv.getSettings().setUseDesktopUserAgent(true); wv.getSettings().setLoadsImagesAutomatically(true); wv.addJavascriptInterface(new ContactsServiceImpl(this), "contactsService"); wv.addJavascriptInterface(new LocationServiceImpl(this), "locationService"); wv.loadUrl("file:///android_asset/index.html"); return wv; }}
@ImplementedBy
@ANIBinding
Example: Linker Generated Manifest
Needed by Android
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="syndroid.demo.SyndroidDemo"><uses-permission android:name="android.permission.ACCESS_LOCATION"/><uses-permission android:name="android.permission.ACCESS_GPS"/><uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS"/> <application> <activity android:name="BootstrapActivity" android:label="SyndroidDemo"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application></manifest>
Example: Linker Generated Build Script
Used to compile and package APK file
mkdir resmkdir classes$ANDROID_HOME/tools/aapt compile -m -J src -M AndroidManifest.xml -I $ANDROID_HOME/android.jarfind src -name '*.java' -print >src.listjavac -cp $ANDROID_HOME/android.jar @src.list -d classes$ANDROID_HOME/tools/dx -JXmx384M --dex --output=classes.dex -- locals=full --positions=lines classes/$ANDROID_HOME/tools/aapt package -f -c -M AndroidManifest.xml -A assets -I $ANDROID_HOME/android.jar SyndroidDemo.apkzip SyndroidDemo.apk classes.dex
Client Stub Generator
• Use @ImplementedBy annotation to find Android implementing class
• Copy this class as resource (for Linker step)• Generate new Client stub
– Generate JSNI native method on Client Stub– JSNI method invokes Javascript object named by
@ANIBinding annotation
Generator to Linker Communication
• Generators work on types, can’t see build artifacts (compiler output)
• Linkers work out build artifacts, can’t explore the type system
• Some Linkers need type information– e.g. Bootstrap scripts, manifests
• Communicate with linker by emitting a build artifact (e.g. text file)
• Linker can scan for this special file and read contents
• In our case, we write out mapping of @ANIBinding to @ImplementedBy
Using Linker Generate Bootstrap/Package for APT
• Read info from Generator (ANIBindings/ImplementedBy)
• Copy implementation classes to build dir• Copy public resources to assets dir• Generate Android Manifest.xml• Generate Android Bootstrap Activity
– Set WebView as view– Enable Javascript– For each ANIBinding, bind instance of
implementing class to Javascript• Run Android Tools
– javac– dx– aapt to create final .apt
Future Directions
• Use Special GWT bootstrap script to detect Android browsers
• Offer user choice of “over the air” provision of Android-specific app
• See http://timepedia.blogspot.com/2008/01/project-syndroidsynthesis-of-gwt-and.html for detailed architecture
Compiling GWT to Flash
Reusing the same Design Pattern for Flash
• Define ActionScriptNativeInterface• Place all “Flash” java code in .flash package
– Use generator to copy it to output as a resource• Use Generator to create JSNI stubs to invoke
Flash methods• But how do we turn Java code into SWF for
the server side?
ActionScript Native InterfaceActionScriptNativeInterface
DrawService
void drawLine(x,y);
extends
DrawServiceClient
void drawLine(int x, int y) { $wnd.flashPlugin.drawLine(x, y);}
implements
Emitted by Generator
package foo.flash;
native void drawLine(int x, int y) /*-{ ... actionscript code to draw line ... }-*/;
impl
Written by Developer
Flash Bootstrap
generate main()
Export drawLine(x,y) via ExternalInterface
Provided by Linker
Compiling Java to ActionScript with GWT
• IDEA: Invoke GWT compiler recursively• Use Linker to create new GWT Project • Copy “flash” Java code to source path • Emit Module.gwt.xml file• Generate entrypoint
– For each ActionScriptNativeInterface add ExternalInterface
• Invoke GWT Compiler to produce SWF?• Emit SWF as output artifact
Packaging GWT output as SWF?
• GWT Compiler produces JS• Avoid GWT APIs which call DOM JSNI
methods– JRE generally safe
• Use Linker to– add custom bootstrap script to invoke EntryPoint– invoke Flex3 SDK in non-strict mode– Emit resulting SWF as output artifact
• Generator+Linker+Generator+Linker Pattern
Writing Gears Workers in GWT
• Reuse same pattern as Flash– e.g. place Gears-translatable Java in ‘.gears’
package• Use Async RPC interfaces• JSON to marshal simple object types to Gears
and back• Use SSO-like Linker
Q&A?
• GwtQuery available at– http://gwtquery.com
• Chronoscope available – http://timepedia.org
• For in-depth articles/code, see blog at:– http://timepedia.blogspot.com
• Upcoming articles on Syndroid, and ActionScriptNativeInterface