128
High Performance JavaScript Nicholas C. Zakas Yahoo!, Inc. October 9, 2010

High Performance JavaScript - Fronteers 2010

Embed Size (px)

DESCRIPTION

For much of its existence, JavaScript has been slow. No one complained until developers created complex web applications with thousands of lines of JavaScript code. Although newer JavaScript engines have improved the situation, there's still a lot to understand about what makes JavaScript slow and what you can do to speed up your code.

Citation preview

Page 1: High Performance JavaScript - Fronteers 2010

High Performance JavaScriptNicholas C. ZakasYahoo!, Inc.

October 9, 2010

Page 2: High Performance JavaScript - Fronteers 2010

Who's this guy?

Principal Front End Engineer

Contributor,Creator of YUI Test

Author Lead Author Contributor Lead Author

Page 3: High Performance JavaScript - Fronteers 2010
Page 4: High Performance JavaScript - Fronteers 2010
Page 5: High Performance JavaScript - Fronteers 2010
Page 6: High Performance JavaScript - Fronteers 2010
Page 7: High Performance JavaScript - Fronteers 2010

@slicknet

(Complaints: @codepo8)

Page 8: High Performance JavaScript - Fronteers 2010

Does JavaScript performance matter?

Page 9: High Performance JavaScript - Fronteers 2010

After all, all browsers now haveoptimizing JavaScript engines

Tracemonkey/JaegarMonkey

(3.5+)

V8(all)

Squirrelfish (4+)

Chakra (9+)

Karakan(10.5+)

Page 10: High Performance JavaScript - Fronteers 2010

So our scripts are getting really, really fast

Page 11: High Performance JavaScript - Fronteers 2010

Old computers ran slow applications Small amounts of CPU power and memory

Page 12: High Performance JavaScript - Fronteers 2010

New computers are generally faster butslow applications still exist

More CPU + more memory = less disciplined application development

Page 13: High Performance JavaScript - Fronteers 2010
Page 14: High Performance JavaScript - Fronteers 2010
Page 15: High Performance JavaScript - Fronteers 2010

It's still possible to write slow JavaScript on the new, faster

JavaScript engines

Page 16: High Performance JavaScript - Fronteers 2010

JavaScript performancedirectly

affects user experience

Page 17: High Performance JavaScript - Fronteers 2010
Page 18: High Performance JavaScript - Fronteers 2010

Where to start?

Page 19: High Performance JavaScript - Fronteers 2010

The UI ThreadThe brains of the operation

Page 20: High Performance JavaScript - Fronteers 2010

The browser UI thread is responsible forboth UI updates and JavaScript execution

Only one can happen at a time

Page 21: High Performance JavaScript - Fronteers 2010

Jobs for UI updates and JavaScript execution areadded to a UI queue if the UI thread is busy

Each job must wait in line for its turn to execute

Page 22: High Performance JavaScript - Fronteers 2010

<button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button>

<script type="text/javascript">window.onload = function(){ document.getElementById("btn").onclick = function(){ //do something };};</script>

Demo!

Page 23: High Performance JavaScript - Fronteers 2010

Before Click

UI Thread

UI Queue

time

Page 24: High Performance JavaScript - Fronteers 2010

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 25: High Performance JavaScript - Fronteers 2010

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Draw down state

Page 26: High Performance JavaScript - Fronteers 2010

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 27: High Performance JavaScript - Fronteers 2010

When Clicked

UI Thread

UI Queue

time

onclick UI UpdateUI Update

Draw up state

Page 28: High Performance JavaScript - Fronteers 2010

No UI updates while JavaScript is executing

Demo!

Page 29: High Performance JavaScript - Fronteers 2010

JavaScript May Cause UI Update

<button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button>

<script type="text/javascript">window.onload = function(){ document.getElementById("btn").onclick = function(){ var div = document.createElement(“div”); div.className = “tip”; div.innerHTML = “You clicked me!”; document.body.appendChild(div); };};</script>

Page 30: High Performance JavaScript - Fronteers 2010

A UI update must use the latest info available

Page 31: High Performance JavaScript - Fronteers 2010

Long-running JavaScript=

Unresponsive UI

Page 32: High Performance JavaScript - Fronteers 2010

Responsive UI

UI Thread

time

JavaScript UI UpdateUI Update

Page 33: High Performance JavaScript - Fronteers 2010

Unresponsive UI

UI Thread

time

JavaScript UI UpdateUI Update

Page 34: High Performance JavaScript - Fronteers 2010

The longer JavaScript runs,the worse the user experience

Page 35: High Performance JavaScript - Fronteers 2010

The runaway script timer prevents JavaScriptfrom running for too long

Each browser imposes its own limit (except Opera)

Page 36: High Performance JavaScript - Fronteers 2010

Internet Explorer

Page 37: High Performance JavaScript - Fronteers 2010

Firefox

Page 38: High Performance JavaScript - Fronteers 2010

Safari

Page 39: High Performance JavaScript - Fronteers 2010

Chrome

Page 40: High Performance JavaScript - Fronteers 2010

http://www.flickr.com/photos/wordridden/426920261/

Page 41: High Performance JavaScript - Fronteers 2010

Runaway Script Timer Limits• Internet Explorer: 5 million statements• Firefox: 10 seconds• Safari: 5 seconds• Chrome: Unknown, hooks into normal crash

control mechanism• Opera: none

Page 42: High Performance JavaScript - Fronteers 2010

Does JIT compiling help?

Page 43: High Performance JavaScript - Fronteers 2010

Interpreted JavaScript

UI Thread

time

Interpret

Page 44: High Performance JavaScript - Fronteers 2010

JITed JavaScript (1st Run)

UI Thread

time

Compile Execute

Page 45: High Performance JavaScript - Fronteers 2010

JITed JavaScript (After 1st Run)

UI Thread

time

Execute

Page 46: High Performance JavaScript - Fronteers 2010

How Long Is Too Long?

“0.1 second [100ms] is about the limit for having the user feel that the system is reacting instantaneously, meaning that no special feedback is necessary except to display the result.”

- Jakob Nielsen

Demo!

Page 47: High Performance JavaScript - Fronteers 2010

Translation:No single JavaScript job should execute for more than 100ms to

ensure a responsive UI

Page 48: High Performance JavaScript - Fronteers 2010

Recommendation:Limit JavaScript execution to no more

than 50ms

measured on IE6 :)

Page 49: High Performance JavaScript - Fronteers 2010

Doing so makes your program awesome

Page 50: High Performance JavaScript - Fronteers 2010

Loadtime TechniquesDon't let JavaScript interfere with page load performance

Page 51: High Performance JavaScript - Fronteers 2010

During page load, JavaScript takes more time on the UI thread

Page 52: High Performance JavaScript - Fronteers 2010

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <script src="foo.js"></script> <p>See ya!</p></body></html>

Page 53: High Performance JavaScript - Fronteers 2010

Result

UI Thread

time

JavaScript UI UpdateUI Update

Page 54: High Performance JavaScript - Fronteers 2010

Result

UI Thread

time

foo.js See ya!Hello world!

Demo!

Page 55: High Performance JavaScript - Fronteers 2010

Result

UI Thread

time

Download See ya!Hello world! Parse Run

The UI thread needs to wait for the script todownload, parse, and run before continuing

Page 56: High Performance JavaScript - Fronteers 2010

Result

UI Thread

Download See ya!Hello world! Parse Run

Download time takes the longest and is variable

Variable Constant

Page 57: High Performance JavaScript - Fronteers 2010

Translation:The page doesn't render while

JavaScript is downloading, parsing, or executing during page load

Page 58: High Performance JavaScript - Fronteers 2010

<!doctype html><html><head> <title>Example</title></head><body> <script src="foo.js"></script> <p>Hello world!</p> <script src="bar.js"></script> <p>See ya!</p> <script src="baz.js"></script> <p>Uh oh!</p></body></html>

Page 59: High Performance JavaScript - Fronteers 2010

Result

UI Thread

time

JavaScript UI UpdateUI Update

The more scripts to download in between UIupdates, the longer the page takes to render

JavaScript JavaScript

Page 60: High Performance JavaScript - Fronteers 2010

Technique #1: Put scripts at the bottom

Page 61: High Performance JavaScript - Fronteers 2010
Page 62: High Performance JavaScript - Fronteers 2010

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <p>See ya!</p> <script src="foo.js"></script></body></html>

Page 63: High Performance JavaScript - Fronteers 2010

Put Scripts at Bottom

UI Thread

time

JavaScriptUI UpdateUI Update

Even if there are multiple scripts, the pagerenders quickly

JavaScript JavaScript

Demo!

Page 64: High Performance JavaScript - Fronteers 2010

Technique #2: Combine JavaScript files

Page 65: High Performance JavaScript - Fronteers 2010

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <p>See ya!</p> <script src="foo.js"></script> <script src="bar.js"></script> <script src="baz.js"></script></body></html>

Page 66: High Performance JavaScript - Fronteers 2010

UI Thread

time

JavaScriptUI Update

Each script has overhead of downloading

JavaScript JavaScript

Page 67: High Performance JavaScript - Fronteers 2010

UI Thread

time

JavaScriptUI Update

Combining all of the files limits the networkoverhead and gets scripts onto the page faster

Page 68: High Performance JavaScript - Fronteers 2010

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <p>See ya!</p> <script src="foo-and-bar-and-baz.js"></script></body></html>

Page 69: High Performance JavaScript - Fronteers 2010

Technique #3: Load scripts dynamically

Page 70: High Performance JavaScript - Fronteers 2010

var script = document.createElement("script"), body;script.type = "text/javascript";script.src = "foo.js";body.appendChild(script, body.firstChild);

Basic Technique

Dynamically loaded scripts are non-blocking

Page 71: High Performance JavaScript - Fronteers 2010

Downloads no longer block the UI thread

Page 72: High Performance JavaScript - Fronteers 2010

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <script src="foo.js"></script> <p>See ya!</p></body></html>

Page 73: High Performance JavaScript - Fronteers 2010

Using HTML <script>

UI Thread

time

Download See ya!Hello world! Parse Run

Page 74: High Performance JavaScript - Fronteers 2010

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <script> var script = document.createElement("script"), body = document.body; script.type = "text/javascript"; script.src = "foo.js"; body.insertBefore(script, body.firstChild); </script> <p>See ya!</p><!-- more content --></body></html>

Page 75: High Performance JavaScript - Fronteers 2010

Using Dynamic Scripts

UI Thread

time

Download

See ya!Hello world!

Parse

Run

Only code execution happens on the UI thread,which means less blocking of UI updates

UI Update

Page 76: High Performance JavaScript - Fronteers 2010

function loadScript(url, callback){var script = document.createElement("script"), body = document.body;script.type = "text/javascript";

if (script.readyState){ //IE <= 8 script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; callback(); } };} else { //Others script.onload = function(){ callback(); };}script.src = url;body.insertBefore(script, body.firstChild);

}

Page 77: High Performance JavaScript - Fronteers 2010

loadScript("foo.js", function(){ alert("Loaded!");});

Usage

Page 78: High Performance JavaScript - Fronteers 2010

Timing Note:Script execution begins immediately after download and parse – timing of

execution is not guaranteed

Page 79: High Performance JavaScript - Fronteers 2010

Using Dynamic Scripts

UI Thread

time

Download

See ya!Hello world!

Parse

Run

Depending on time to download and script size,execution may happen before next UI update

UI Update

Page 80: High Performance JavaScript - Fronteers 2010

Technique #4: Defer scripts

Page 81: High Performance JavaScript - Fronteers 2010

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <script defer src="foo.js"></script> <p>See ya!</p> <!-- even more markup --></body></html>

Page 82: High Performance JavaScript - Fronteers 2010

SoonSoon3.53.5 SoonSoon ??4.04.0

Support for <script defer>

Page 83: High Performance JavaScript - Fronteers 2010

Deferred scripts begin to download immediately,

but don't execute until all UI updates complete

(DOMContentLoaded)

Page 84: High Performance JavaScript - Fronteers 2010

Using <script defer>

UI Thread

time

Download

See ya!Hello world!

Parse

Run

Similar to dynamic script nodes, but with aguarantee that execution will happen last

More UI More UI

Page 85: High Performance JavaScript - Fronteers 2010

Timing Note:Although scripts always execute after

UI updates complete, the order of multiple <script defer> scripts is not

guaranteed across browsers

Page 86: High Performance JavaScript - Fronteers 2010

Technique #5: Asynchronous scripts

Page 87: High Performance JavaScript - Fronteers 2010

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <script async src="foo.js"></script> <p>See ya!</p> <!-- even more markup --></body></html>

Page 88: High Performance JavaScript - Fronteers 2010

SoonSoon3.63.6 SoonSoon ????

Support for <script async>

Page 89: High Performance JavaScript - Fronteers 2010

Asynchronous scripts behave a lot like dynamic scripts

Page 90: High Performance JavaScript - Fronteers 2010

Using <script async>

UI Thread

time

Download

See ya!Hello world!

Parse

Run

Download begins immediately and execution isslotted in at first available spot

UI Update

Page 91: High Performance JavaScript - Fronteers 2010

Note:Order of execution is explicitly not preserved for asynchronous scripts

Page 92: High Performance JavaScript - Fronteers 2010

Runtime TechniquesWays to ensure JavaScript doesn't run away

Page 93: High Performance JavaScript - Fronteers 2010

function processArray(items, process, callback){ for (var i=0,len=items.length; i < len; i++){ process(items[i]); } callback();}

Page 94: High Performance JavaScript - Fronteers 2010

Technique #1: Timers

Page 95: High Performance JavaScript - Fronteers 2010

JavaScript Timers

• Created using setTimeout()• Schedules a new JavaScript execution job for

some time in the future• When the delay is up, the job is added to the

UI queue– Note: This does not guarantee execution

after the delay, just that the job is added to the UI queue and will be executed when appropriate

Page 96: High Performance JavaScript - Fronteers 2010

JavaScript Timers

• For complex processing, split up into timed functionality

• Use timers to delay some processing for later

Page 97: High Performance JavaScript - Fronteers 2010

function timedProcessArray(items, process, callback){ //create a clone of the original

var todo = items.concat(); setTimeout(function(){ var start = +new Date(); do { process(todo.shift()); } while (todo.length > 0 && (+new Date() - start < 50)); if (todo.length > 0){ setTimeout(arguments.callee, 25); } else { callback(items); } }, 25);} Demo(s)!

Page 98: High Performance JavaScript - Fronteers 2010

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 99: High Performance JavaScript - Fronteers 2010

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 100: High Performance JavaScript - Fronteers 2010

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 101: High Performance JavaScript - Fronteers 2010

When Clicked

UI Thread

UI Queue

time

UI UpdateUI Update onclick

Page 102: High Performance JavaScript - Fronteers 2010

After 25ms

UI Thread

UI Queue

time

UI UpdateUI Update onclick

JavaScript

Page 103: High Performance JavaScript - Fronteers 2010

After 25ms

UI Thread

UI Queue

time

UI UpdateUI Update onclick JavaScript

Page 104: High Performance JavaScript - Fronteers 2010

After Another 25ms

UI Thread

UI Queue

time

UI UpdateUI Update onclick JavaScript

JavaScript

Page 105: High Performance JavaScript - Fronteers 2010

After Another 25ms

UI Thread

UI Queue

time

UI UpdateUI Update onclick JavaScript JavaScript

Page 106: High Performance JavaScript - Fronteers 2010

Technique #2: Web Workers

Page 107: High Performance JavaScript - Fronteers 2010
Page 108: High Performance JavaScript - Fronteers 2010

Web Workers

• Asynchronous JavaScript execution• Execution happens in a separate process

– Not on the UI thread = no UI delays• Data-driven API

– Data is serialized when sending data into or out of Worker

– No access to DOM, BOM– Completely separate execution

environment

Page 109: High Performance JavaScript - Fronteers 2010

//in pagevar worker = new Worker("process.js");worker.onmessage = function(event){ useData(event.data);};worker.postMessage(values);

//in process.jsself.onmessage = function(event){ var items = event.data; for (var i=0,len=items.length; i < len; i++){ process(items[i]); } self.postMessage(items);}; Demo!

Page 110: High Performance JavaScript - Fronteers 2010

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 111: High Performance JavaScript - Fronteers 2010

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 112: High Performance JavaScript - Fronteers 2010

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 113: High Performance JavaScript - Fronteers 2010

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Worker Thread

Page 114: High Performance JavaScript - Fronteers 2010

When Clicked

UI Thread

UI Queue

time

UI UpdateUI Update onclick

Worker Thread

JavaScript

Page 115: High Performance JavaScript - Fronteers 2010

Worker Thread Complete

UI Thread

UI Queue

time

UI UpdateUI Update onclick

onmessage

Page 116: High Performance JavaScript - Fronteers 2010

Worker Thread Complete

UI Thread

UI Queue

time

UI UpdateUI Update onclick onmessage

Page 117: High Performance JavaScript - Fronteers 2010

4.04.03.53.5 4.04.0 10.610.6??

Support for Web Workers

Page 118: High Performance JavaScript - Fronteers 2010

Recap

Page 119: High Performance JavaScript - Fronteers 2010
Page 120: High Performance JavaScript - Fronteers 2010

The browser UI thread is responsible forboth UI updates and JavaScript execution

Only one can happen at a time

Page 121: High Performance JavaScript - Fronteers 2010

Responsive UI

UI Thread

time

JavaScript UI UpdateUI Update

Page 122: High Performance JavaScript - Fronteers 2010

Unresponsive UI

UI Thread

time

JavaScript UI UpdateUI Update

Page 123: High Performance JavaScript - Fronteers 2010

Avoid Slow Loading JavaScript• Put scripts at the bottom• Concatenate scripts into as few files as

possible• Choose the right way to load your scripts

– Dynamically created scripts– Deferred scripts– Asynchronous scripts

Page 124: High Performance JavaScript - Fronteers 2010

Avoid Slow JavaScript• Don't allow JavaScript to execute for more

than 50ms• Break up long JavaScript processes using:

– Timers– Web Workers

Page 125: High Performance JavaScript - Fronteers 2010

The End

Page 126: High Performance JavaScript - Fronteers 2010

Etcetera• My blog:

www.nczonline.net

• Twitter:@slicknet

• These Slides:http://slideshare.net/nzakas/presentations/

• Rate Me:http://spkr8.com/t/4568

Page 127: High Performance JavaScript - Fronteers 2010

Questions?

Page 128: High Performance JavaScript - Fronteers 2010

Creative Commons Images Used• http://www.flickr.com/photos/8628950@N06/1332225362/

• http://www.flickr.com/photos/hippie/2406411610/

• http://www.flickr.com/photos/55733754@N00/3325000738/

• http://www.flickr.com/photos/eurleif/255241547/

• http://www.flickr.com/photos/off_the_wall/3444915939/

• http://www.flickr.com/photos/wwarby/3296379139/

• http://www.flickr.com/photos/derekgavey/4358797365/

• http://www.flickr.com/photos/mulad/286641998/