Saturday, July 20, 2013

Bare Minimum: File Size and Numbers for Dart Deployment (Application Cache)


Following up on last night's deployment improvements in the Dart version of the ICE Code Editor, I hope to be able to finish up today. Whereas yesterday I shrunk the application payload with the --minify option in dart2js, today I would like to shrink the payload by being a bit more judicious with what needs to be downloaded.

Taking a look at http://gamingjs.com/ice-beta/ (which currently holds the Dart version of ICE) in an incognito browser session, I find:


18 requests  ❘  190 KB transferred  ❘  1.66 s (load: 1.64 s, DOMContentLoaded: 139 ms)
In some respects, that is not bad. This is the non-minified version of ICE, meaning that the compiled JavaScript is 912 KB alone. That, plus the 17 other requests that are needed to download ICE should push the payload up to well over 1 MB. Since the actual data transferred is only 190 KB, it would seem that gzip encoding of the resources is producing some nice bandwidth savings. Combined that with the 60% decrease in the compiled JavaScript from the --minify option, and ICE should be in fine shape.

And yet…

There is an obvious gap in the transfer of files. What gives? And if I can eliminate it, will the download go even faster?

The answer to the first question—the cause of the gap in transferring files—lives in the JavaScript console. There are many lines of output (around 100, in fact) ending with:
...
Application Cache Progress event (95 of 100) http://gamingjs.com/ice-beta/part.js.map
Application Cache Progress event (96 of 100) http://gamingjs.com/ice-beta/packages/unittest/src/utils.dart
Application Cache Progress event (97 of 100) http://gamingjs.com/ice-beta/packages/ice_code_editor/full/rename_dialog.dart
Application Cache Progress event (98 of 100) http://gamingjs.com/ice-beta/packages/unittest/src/test_case.dart
Application Cache Progress event (99 of 100) http://gamingjs.com/ice-beta/packages/js/src/wrapping/typed_proxy.dart
Application Cache Progress event (100 of 100) 
Application Cache Cached event
Document was loaded from Application Cache with manifest http://gamingjs.com/ice-beta/editor.appcache
So it seems that I may be putting just a little too much into application cache. Well, maybe a lot. Considering that loading the editor and the default project only required 18 requests, I am almost certainly loading way too much.

A closer inspection shows that #96 and #98 of the application cache files are unit test files—those certainly are not needed in application cache. They probably do not need to exist on the server at all. But even more so, there is absolutely no reason to jam a single Dart file into application cache. There is not a single production browser that supports raw Dart, so I am forcing ICE users on http://gamingjs.com/ice-beta to download a bunch of files for no reason whatsoever. Sorry about that.

Application cache beasts, like the ICE Code Editor, need a manifest file on the document's <html> tag:
<!DOCTYPE html>
<html manifest="editor.appcache">
  <head>
    <title>code editor</title>
    <meta charset="utf-8">
    <script src="appcache.js"></script>
    <script src="packages/browser/dart.js"></script>
    <script src="packages/browser/interop.js"></script>
    <script src="main.dart" type="application/dart"></script>
  </head>
  <body>
  </body>
</html>
This manifest file informs browsers what they need to down for the application to function properly:
CACHE MANIFEST
# 2013-07-10

CACHE:
/favicon.ico
/Three.js
/Tween.js
/Detector.js
/physi.js
/Mouse.js
/Scoreboard.js
/ChromeFixes.js
/ammo.js
/physijs_worker.js

part.js
main.dart
index.html
main.dart.js.map
main.dart.js
packages/js/js.dart
packages/js/src/wrapping/js/object_to_map_adapter.dart
# Many, many, many more files...

NETWORK:
*
The asterisk in the NETWORK section of my manifest says that anything not explicitly listed in the CACHE session should downloaded over the network. I really ought to allow all of the Dart code, along with any supporting resources, to get served up over the network. I can still leave them on the server, in case a Dartium browser comes along, but not place it in application cache.

After excluding a bunch of files, I get my list down from 100 to 33:
➜  ice-beta git:(gh-pages) ✗ cat editor.appcache | grep -v \\.dart$ | \
  grep -v pubspec\\. | \
  grep -v unittest | \
  grep -v inconsolata | \
  grep \\. | \
  wc -l
33
That is much, much better because, as everyone knows, individual files have a much greater impact on download speeds than do large files.

I find that I can generate the same list of worthy application files with a grep statement that whitelists HTML, JavaScript, and source map files:
➜  ice-beta git:(gh-pages) ✗ find | \
  grep -e \\.js$ -e \\.map$ -e \\.html$ -e \\.css$ | \
  grep -v -e unittest -e /lib/ | \
  wc -l           
22
There is some duplication in the lib/ sub-directory that I do not fully understand (it may just be how I coped it last night). That is something for another day. For now I ignore them in the grep -v (ignore) statement.

I am explicitly ignoring the .deps file that dart2js generates. I should probably dot-gitignore that file—it includes local file URLs:
file:///home/chris/local/dart/dart-sdk/lib/_collection_dev/arrays.dart
file:///home/chris/local/dart/dart-sdk/lib/_collection_dev/collection_dev.dart
...

I do need to manually add the main.dart script to the application cache manifest—the browser does request it even if it does not know how to process it (because it is in a <script> tag in the HTML). With those changes, my entire manifest file contains:
CACHE MANIFEST
# 2013-07-20

CACHE:
/favicon.ico
/Three.js
/Tween.js
/Detector.js
/physi.js
/Mouse.js
/Scoreboard.js
/ChromeFixes.js
/ammo.js
/physijs_worker.js

main.dart

part.js
index.html
main.dart.js.map
main.dart.js
packages/js/dart_interop.js
packages/browser/interop.js
packages/browser/dart.js
packages/ice_code_editor/js/deflate/rawinflate.js
packages/ice_code_editor/js/deflate/rawdeflate.js
packages/ice_code_editor/js/ace/mode-css.js
packages/ice_code_editor/js/ace/ace.js
packages/ice_code_editor/js/ace/theme-textmate.js
packages/ice_code_editor/js/ace/theme-chrome.js
packages/ice_code_editor/js/ace/ext-searchbox.js
packages/ice_code_editor/js/ace/mode-html.js
packages/ice_code_editor/js/ace/worker-javascript.js
packages/ice_code_editor/js/ace/keybinding-emacs.js
packages/ice_code_editor/js/ace/mode-javascript.js
packages/ice_code_editor/css/ice.css
packages/ice_code_editor/html/preview_frame.html
part.js.map
appcache.js

NETWORK:
*
After pushing that to http://gamingjs.com/ice-beta and firing up a new incognito window, I find:


18 requests  ❘  103 KB transferred  ❘  1.39 s (load: 1.37 s, DOMContentLoaded: 187 ms)

That is a 16% decrease in load time when compared to the previous Dart version of ICE, but it is still 30% slower than the old pure JavaScript version of ICE.

Ultimately it does not matter too much. I am still looking at a load time of less than 1.5 seconds for the first access, which is pretty darn good. Subsequent access will be even better since it never even touches the the network—the benefits of being stored in application cache. The dart2js tool is only going to make smaller compiled JavaScript, though I would guess that the changes from here on out will be smaller—that we will not see compiled Dart smaller by many orders of magnitude.

And if I find that I need much better, there is always SPDY!


Day #818

No comments:

Post a Comment