Sei sulla pagina 1di 51

GWT Extreme!

Performance and Flexibility


Ray Cromwell, CTO, Timefire, Inc
05/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 and Smaller than Possible” Code


• 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
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 transitions
Sample 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 Google Yahoo


Desktop

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 Interface
AndroidNativeInterface
Written by Developer
extends LocationServiceImpl

LocationService String getLocation() {


impl
return ...android native code...
String getLocation(); }

implements
LocationServiceClient Android Bootstrap

String getLocation() { Bind


return $wnd.locationService.getLocation(); LocationServiceImpl
} to
window.locationService

Emitted by Generator 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); @ImplementedBy
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;
}
}

@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.jar
find src -name '*.java' -print >src.list
javac -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.apk
zip 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 Interface
ActionScriptNativeInterface
Written by Developer
extends package foo.flash;

DrawService native void drawLine(int x, int y) /*-{


impl
... actionscript code to draw line ...
void drawLine(x,y); }-*/;

implements
DrawServiceClient Flash Bootstrap

void drawLine(int x, int y) { generate main()


$wnd.flashPlugin.drawLine(x, y);
} Export drawLine(x,y)
via ExternalInterface

Emitted by Generator 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

Potrebbero piacerti anche