Optimizing JavaScript for Chrome

A recent discussion with a tech guru at Automattic posed a great question — how does JavaScript asynchronous processing work on a single-threaded app?  Great question, but before I found out how that worked I decided to refresh my knowledge on how JavaScript manages the call stack.   Turns out a LOT has changed in 10 years and it turns out Google’s V8 engine was launched.

What is V8?

V8 was very likely a result of Google Maps.    Google Maps was one of the most-used JavaScript applications to hit the Internet (and has been my primary focus for the past 5 years as part of my Store Locator Plus project).  The problem with Google Maps is that the JavaScript library behind it is HUGE.    So big that it crushed most browsers of the day.   Not only did laptops and mobile devices have far less compute power — the JavaScript engines were not very efficient.

Enter V8.   Back in 2008 Google launched V8 as a new JavaScript engine for Chrome.   They employed several tricks to speed up code execution making their maps, and every other JavaScript applet, run faster.     It also happens to have become the core engine used in NodeJS which has itself become another “killer JS app”.

V8 does a few things like just-in-time compilation of code, basically condensing the code into less human-friendly script that is much faster for the engine to execute.   They added threads for garbage collection, code refactoring, and code optimization.    This last part is where I learned a few things that can be used to create more efficient, and thus faster-executing, JavaScript code.

Optimizing Javascript

These tips all come directly from the folks at SessionStack where they focus on the internals of the V8 JavaScript engine, however many of these tips will work with other engines like SpiderMonkey (Firefox), Nitro (Safari), and Chakra (Microsoft offspring).

Set the properties in the same order

When you are interacting with an object set the properties in the same order every time. If you create a new google.maps.LatLng object make it a habit to always set lat first then lng.

This allows the hidden objects manager (part of the V8 optimization engine) to re-use the same hidden object over-and-over.

This creates a hidden object , let’s call it “map_latlng_lat_then_lng” (in reality it is more likely to be named human unfriendly “C9”).

var newPlace = new google.maps.LatLng();
newPlace.lat = -89.0;
newplace.lng = 12.1;

If you then immediately run this code creates another hidden object, “map_latlng_lng_then_lat” even though the properties are identical.

var newPlace = new google.maps.LatLng();
newplace.lng = 12.1;
newPlace.lat = -89.0;

A more interesting example – this will re-use the same “C9” hidden object for both making things run faster:

var myPlace = new google.maps.LatLng();
newPlace.lat = -89.0;
newplace.lng = 12.1;

var yourPlace = new google.maps.LatLng();
yourPlace.lng = -78.6;
yourPlace.lng = 23.4;

Both examples are over-simplified and not best practices code examples — the take-away is to keep your properties being set in the same order whenever possible.

Define properties in constructors

Instead of building objects and then defining the properties, as we have done above, try to set all your properties in the object constructor itself.

function createEvent( who, what, why ) {
    this.who = who;
    this.what = what;
    this.why = why;
    this.when = 'when;
    this.where = 'here';
}

var event = createEvent( 'me', 'writing' , 'community contribution' );

If possible use the contractor parameters for pre-defined methods which will employ this technique if they are part of a well-defined library like the Google Maps JavaScript API.

var homePoint = new google.maps.LatLng( slplus.options.map_center_lat ,slplus.options.map_center_lng );

This all ties back to our first rule allowing re-use of hidden classes.

Avoid run-once methods

If a method is only called from one place put it inline for better performance.

While this is likely a good idea from a performance perspective, I personally find that very long functions with lots of inline code are hard to maintain.   If you are not using building tools like Webpack or Bower you will need to decide between performance and maintainability – a classic trade-off.

Avoid sparse arrays

When possible use arrays with incremental keys.   They are more efficient than hashes.   In other words, if you don’t NEED key/value pairs from a hash table then use a simple linear array.

Also, don’t delete array elements from the middle of an array.   Pop/shift instead otherwise your linear array is not a sparse array.

Share your hints

Have other JavaScript performance hints to share?  Comment below!

 

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.