Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

High Performance JavaScript 2011

15,366 views

Published on

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.

Published in: Technology

High Performance JavaScript 2011

  1. 1. High Performance JavaScript<br />Nicholas C. Zakas| nczonline.net<br />
  2. 2. Who's this guy?<br />Co-Creator csslint.net<br />Contributor,<br />Creator of YUI Test<br />Ex-tech lead<br />Yahoo!<br />Author<br />Lead Author<br />Contributor<br />Lead Author<br />
  3. 3. @slicknet<br />(complaints: @brendaneich)<br />
  4. 4. Does JavaScript performance matter?<br />
  5. 5.
  6. 6.
  7. 7.
  8. 8. All browsers now haveoptimizing JavaScript engines<br />Tracemonkey/<br />JaegarMonkey<br />(3.5+)<br />V8<br />(all)<br />Nitro<br />(4+)<br />Chakra<br />(9+)<br />Karakan<br />(10.5+)<br />
  9. 9. http://ie.microsoft.com/testdrive/benchmarks/sunspider/default.html<br />
  10. 10. Old computers ran slow applications<br />Small amounts of CPU power and memory<br />
  11. 11. New computers are generally faster but<br />slow applications still exist<br />More CPU + more memory = less disciplined application development<br />
  12. 12.
  13. 13. Oh yeah, one more thing<br />
  14. 14.
  15. 15.
  16. 16.
  17. 17. It's still possible to write slow JavaScript<br />
  18. 18. JavaScript performancedirectlyaffects user experience<br />
  19. 19. The UI Threadaka Browser Event Loop<br />
  20. 20. The browser UI thread is responsible for<br />both UI updates and JavaScript execution<br />Only one can happen at a time<br />
  21. 21. Jobs for UI updates and JavaScript execution<br />are added to a UI queue<br />Each job must wait in line for its turn to execute<br />
  22. 22. <button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button><br /><script type="text/javascript"><br />window.onload = function(){<br /> document.getElementById("btn").onclick = function(){<br />//do something<br /> };<br />};<br /></script><br />
  23. 23. Before Click<br />UI Thread<br />time<br />UI Queue<br />
  24. 24. When Clicked<br />UI Thread<br />time<br />UI Queue<br />UI Update<br />onclick<br />UI Update<br />
  25. 25. When Clicked<br />UI Thread<br />UI Update<br />time<br />UI Queue<br />onclick<br />Draw down state<br />UI Update<br />
  26. 26. When Clicked<br />UI Thread<br />onclick<br />UI Update<br />time<br />UI Queue<br />UI Update<br />
  27. 27. When Clicked<br />UI Thread<br />onclick<br />UI Update<br />UI Update<br />time<br />UI Queue<br />Draw up state<br />
  28. 28. No UI updates while JavaScript is executing<br />
  29. 29. JavaScript May Cause UI Update<br /><button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button><br /><script type="text/javascript"><br />window.onload = function(){<br /> document.getElementById("btn").onclick = function(){<br /> var div = document.createElement(“div”);<br /> div.className = “tip”;<br /> div.innerHTML = “You clicked me!”;<br /> document.body.appendChild(div);<br /> };<br />};<br /></script><br />
  30. 30. A UI update must use the latest info available<br />
  31. 31. Long-running JavaScript=Unresponsive UI<br />
  32. 32. Responsive UI<br />UI Thread<br />JavaScript<br />UI Update<br />UI Update<br />time<br />
  33. 33. Unresponsive UI<br />UI Thread<br />JavaScript<br />UI Update<br />UI Update<br />time<br />
  34. 34. The longer JavaScript runs,the worse the user experience<br />
  35. 35. The runaway script timer prevents JavaScript<br />from running for too long<br />Each browser imposes its own limit (except Opera)<br />
  36. 36. Internet Explorer<br />
  37. 37. Firefox<br />
  38. 38. Safari<br />
  39. 39. Chrome<br />
  40. 40.
  41. 41. Runaway Script Timer Limits<br />Internet Explorer: 5 million statements<br />Firefox: 10 seconds<br />Safari: 5 seconds<br />Chrome: Unknown, hooks into normal crash control mechanism<br />Opera: none<br />
  42. 42. How Long Is Too Long?<br />“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.”<br />- Jakob Nielsen<br />
  43. 43. Translation:No single JavaScript job should execute for more than 100ms to ensure a responsive UI<br />
  44. 44. Recommendation:Limit JavaScript execution to no more than 50msmeasured on IE6 :)<br />
  45. 45. Does JIT compiling help?<br />
  46. 46. Interpreted JavaScript<br />UI Thread<br />Interpret<br />time<br />
  47. 47. JITed JavaScript (1st Run)<br />UI Thread<br />Compile<br />Execute<br />time<br />
  48. 48. JITed JavaScript (After 1st Run)<br />UI Thread<br />Execute<br />time<br />
  49. 49. Loadtime TechniquesDon't let JavaScript interfere with page load performance<br />
  50. 50. During page load, JavaScript takes more time on the UI thread<br />
  51. 51. <!doctype html><br /><html><br /><head><br /> <title>Example</title><br /></head><br /><body><br /> <p>Hello world!</p><br /><script src="foo.js"></script><br /> <p>See ya!</p><br /></body><br /></html><br />
  52. 52. Result<br />UI Thread<br />JavaScript<br />UI Update<br />UI Update<br />time<br />
  53. 53. Result<br />UI Thread<br />foo.js<br />See ya!<br />Hello world!<br />time<br />
  54. 54. Result<br />UI Thread<br />Download<br />See ya!<br />Hello world!<br />Parse<br />Run<br />time<br />The UI thread needs to wait for the script to<br />download, parse, and run before continuing<br />
  55. 55. Result<br />UI Thread<br />Download<br />See ya!<br />Hello world!<br />Parse<br />Run<br />Variable<br />Constant<br />Download time takes the longest and is variable<br />
  56. 56. Translation:The page doesn't render while JavaScript is downloading, parsing, or executing during page load<br />
  57. 57. <!doctype html><br /><html><br /><head><br /> <title>Example</title><br /></head><br /><body><br /><script src="foo.js"></script><br /> <p>Hello world!</p><br /><script src="bar.js"></script><br /> <p>See ya!</p><br /><script src="baz.js"></script><br /> <p>Uh oh!</p><br /></body><br /></html><br />
  58. 58. Result<br />UI Thread<br />JavaScript<br />UI Update<br />UI Update<br />JavaScript<br />JavaScript<br />time<br />The more scripts to download in between UI<br />updates, the longer the page takes to render<br />
  59. 59. Technique #1: Put scripts at the bottom<br />
  60. 60.
  61. 61. <!doctype html><br /><html><br /><head><br /> <title>Example</title><br /></head><br /><body><br /> <p>Hello world!</p><br /> <p>See ya!</p><br /><script src="foo.js"></script><br /></body><br /></html><br />
  62. 62. Put Scripts at Bottom<br />UI Thread<br />JavaScript<br />UI Update<br />UI Update<br />JavaScript<br />JavaScript<br />time<br />Even if there are multiple scripts, the page<br />renders quickly<br />
  63. 63. Technique #2: Combine JavaScript files<br />
  64. 64. <!doctype html><br /><html><br /><head><br /> <title>Example</title><br /></head><br /><body><br /> <p>Hello world!</p><br /> <p>See ya!</p><br /><script src="foo.js"></script><br /> <script src="bar.js"></script><br /> <script src="baz.js"></script><br /></body><br /></html><br />
  65. 65. UI Thread<br />JavaScript<br />UI Update<br />JavaScript<br />JavaScript<br />time<br />Each script has overhead of downloading<br />
  66. 66. UI Thread<br />JavaScript<br />UI Update<br />time<br />Combining all of the files limits the network<br />overhead and gets scripts onto the page faster<br />
  67. 67. <!doctype html><br /><html><br /><head><br /> <title>Example</title><br /></head><br /><body><br /> <p>Hello world!</p><br /> <p>See ya!</p><br /> <script src="foo-and-bar-and-baz.js"></script><br /></body><br /></html><br />
  68. 68. Technique #3: Load scripts dynamically<br />
  69. 69. Basic Technique<br />var script = document.createElement("script"),<br /> body;<br />script.type = "text/javascript";<br />script.src = "foo.js";<br />body.insertBefore(script, body.firstChild);<br />Dynamically loaded scripts are non-blocking<br />
  70. 70. Downloads no longer block the UI thread<br />
  71. 71. <!doctype html><br /><html><br /><head><br /> <title>Example</title><br /></head><br /><body><br /> <p>Hello world!</p><br /><script src="foo.js"></script><br /> <p>See ya!</p><br /></body><br /></html><br />
  72. 72. Using HTML <script><br />UI Thread<br />Download<br />See ya!<br />Hello world!<br />Parse<br />Run<br />time<br />
  73. 73. <!doctype html><br /><html><br /><head><br /> <title>Example</title><br /></head><br /><body><br /> <p>Hello world!</p><br /> <script><br /> var script = document.createElement("script"),<br /> body = document.body;<br /> script.type = "text/javascript";<br /> script.src = "foo.js";<br /> body.insertBefore(script, body.firstChild);<br /> </script><br /> <p>See ya!</p><!-- more content --><br /></body><br /></html><br />
  74. 74. Using Dynamic Scripts<br />UI Thread<br />See ya!<br />Hello world!<br />UI Update<br />Run<br />time<br />Download<br />Parse<br />Only code execution happens on the UI thread,<br />which means less blocking of UI updates<br />
  75. 75. function loadScript(url, callback){var script = document.createElement("script"), body = document.body;script.type = "text/javascript";<br />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);<br />}<br />
  76. 76. Usage<br />loadScript("foo.js", function(){<br /> alert("Loaded!");<br />});<br />
  77. 77. Timing Note:Script execution begins immediately after download and parse – timing of execution is not guaranteed<br />
  78. 78. Technique #4: Defer scripts<br />
  79. 79. <!doctype html><br /><html><br /><head><br /> <title>Example</title><br /></head><br /><body><br /> <p>Hello world!</p><br /><script defer src="foo.js"></script><br /> <p>See ya!</p><br /> <!-- even more markup --><br /></body><br /></html><br />
  80. 80. Support for <script defer><br />?<br />5.0<br />3.5<br />7.0<br />4.0<br />
  81. 81. Deferred scripts begin to download immediately, but don't execute until all UI updates complete<br />
  82. 82. Using <script defer><br />UI Thread<br />See ya!<br />Hello world!<br />More UI<br />Run<br />More UI<br />time<br />Download<br />Parse<br />Similar to dynamic script nodes, but with a<br />guarantee that execution will happen last<br />
  83. 83. Timing Note:Although scripts always execute after UI updates complete, the order of multiple <script defer> scripts is not guaranteed across browsers<br />
  84. 84. Technique #5: Asynchronous scripts<br />
  85. 85. <!doctype html><br /><html><br /><head><br /> <title>Example</title><br /></head><br /><body><br /> <p>Hello world!</p><br /><script async src="foo.js"></script><br /> <p>See ya!</p><br /> <!-- even more markup --><br /></body><br /></html><br />
  86. 86. Support for <script async><br />?<br />5.0<br />3.6<br />7.0<br />10<br />
  87. 87. Asynchronous scripts behave a lot like dynamic scripts<br />
  88. 88. Using <script async><br />UI Thread<br />See ya!<br />Hello world!<br />UI Update<br />Run<br />time<br />Download<br />Parse<br />Download begins immediately and execution is<br />slotted in at first available spot<br />
  89. 89. Note:Order of execution is explicitly not preserved for asynchronous scripts<br />
  90. 90. Runtime TechniquesWays to ensure JavaScript doesn't run away<br />
  91. 91. function processArray(items, process, callback){<br />for (var i=0,len=items.length; i < len; i++){<br /> process(items[i]);<br /> }<br /> callback();<br />}<br />
  92. 92. Technique #1: Timers<br />
  93. 93. //create a new timer and delay by 500ms<br />setTimeout(function(){<br />//code to execute here<br />}, 500);<br />setTimeout() schedules a function to be added to the UI queue after a delay<br />
  94. 94. function timedProcessArray(items, process, callback){<br />//create a clone of the original var todo = items.concat();<br /> setTimeout(function(){<br />var start = +new Date();<br />do {<br /> process(todo.shift());<br /> } while (todo.length > 0 &&<br /> (+new Date() - start < 50));<br />if (todo.length > 0){<br /> setTimeout(arguments.callee, 25);<br /> } else {<br /> callback(items);<br /> }<br /> }, 25);<br />}<br />
  95. 95. When Clicked<br />UI Thread<br />time<br />UI Queue<br />UI Update<br />onclick<br />UI Update<br />
  96. 96. When Clicked<br />UI Thread<br />UI Update<br />time<br />UI Queue<br />onclick<br />UI Update<br />
  97. 97. When Clicked<br />UI Thread<br />onclick<br />UI Update<br />time<br />UI Queue<br />UI Update<br />
  98. 98. When Clicked<br />UI Thread<br />UI Update<br />UI Update<br />onclick<br />time<br />UI Queue<br />
  99. 99. After 25ms<br />UI Thread<br />UI Update<br />UI Update<br />onclick<br />time<br />UI Queue<br />JavaScript<br />
  100. 100. After 25ms<br />UI Thread<br />JavaScript<br />UI Update<br />UI Update<br />onclick<br />time<br />UI Queue<br />
  101. 101. After Another 25ms<br />UI Thread<br />JavaScript<br />UI Update<br />UI Update<br />onclick<br />time<br />UI Queue<br />JavaScript<br />
  102. 102. After Another 25ms<br />UI Thread<br />JavaScript<br />JavaScript<br />UI Update<br />UI Update<br />onclick<br />time<br />UI Queue<br />
  103. 103. Technique #2: Script Yielding<br />NEW!<br />
  104. 104.
  105. 105. //delay a function until after UI updates are done<br />setImmediate(function(){<br />//code to execute here<br />});<br />setImmediate() adds code to the UI queue after pending UI updates are finished<br />
  106. 106. Support for Script Yielding<br />?<br />?<br />?<br />?<br />10<br />msSetIntermediate()<br />
  107. 107. functionyieldingProcessArray(items, process, callback){<br />//create a clone of the originalvartodo = items.concat();<br />setImmediate(function(){<br />var start = +new Date();<br />do {<br /> process(todo.shift());<br /> } while (todo.length > 0 &&<br /> (+new Date() - start < 50));<br />if (todo.length > 0){<br />setImmediate(arguments.callee);<br /> } else {<br /> callback(items);<br /> }<br />});<br />}<br />
  108. 108. When Clicked<br />UI Thread<br />time<br />UI Queue<br />UI Update<br />onclick<br />UI Update<br />
  109. 109. When Clicked<br />UI Thread<br />UI Update<br />time<br />UI Queue<br />onclick<br />UI Update<br />
  110. 110. When Clicked<br />UI Thread<br />onclick<br />UI Update<br />time<br />UI Queue<br />UI Update<br />
  111. 111. When Clicked<br />UI Thread<br />UI Update<br />UI Update<br />onclick<br />time<br />UI Queue<br />
  112. 112. After Last UI Update<br />UI Thread<br />UI Update<br />UI Update<br />onclick<br />time<br />UI Queue<br />JavaScript<br />
  113. 113. After Last UI Update<br />UI Thread<br />JavaScript<br />UI Update<br />UI Update<br />onclick<br />time<br />UI Queue<br />
  114. 114. No Other UI Updates<br />UI Thread<br />JavaScript<br />UI Update<br />UI Update<br />onclick<br />time<br />UI Queue<br />JavaScript<br />
  115. 115. No Other UI Updates<br />UI Thread<br />JavaScript<br />JavaScript<br />UI Update<br />UI Update<br />onclick<br />time<br />UI Queue<br />
  116. 116. Technique #3: Web Workers<br />
  117. 117.
  118. 118. Support for Web Workers<br />10.6<br />4.0<br />3.5<br />4.0<br />10<br />
  119. 119. Web Workers<br />Asynchronous JavaScript execution<br />Execution happens outside the UI thread<br />Doesn’t block UI updates<br />Data-Driven API<br />Data is serialized going into and out of the worker<br />No access to DOM or BOM<br />Separate execution environment<br />
  120. 120. //in page<br />var worker = new Worker("process.js");<br />worker.onmessage = function(event){<br /> useData(event.data);<br />};<br />worker.postMessage(values);<br />//in process.js<br />self.onmessage = function(event){<br />var items = event.data;<br />for (var i=0,len=items.length; i < len; i++){<br /> process(items[i]);<br /> }<br /> self.postMessage(items);<br />};<br />
  121. 121. When Clicked<br />UI Thread<br />time<br />UI Queue<br />UI Update<br />onclick<br />UI Update<br />
  122. 122. When Clicked<br />UI Thread<br />UI Update<br />time<br />UI Queue<br />onclick<br />UI Update<br />
  123. 123. When Clicked<br />UI Thread<br />onclick<br />UI Update<br />time<br />UI Queue<br />UI Update<br />
  124. 124. When Clicked<br />UI Thread<br />onclick<br />UI Update<br />time<br />UI Queue<br />Worker Thread<br />UI Update<br />
  125. 125. When Clicked<br />UI Thread<br />UI Update<br />UI Update<br />onclick<br />time<br />UI Queue<br />Worker Thread<br />JavaScript<br />
  126. 126. Worker Thread Complete<br />UI Thread<br />UI Update<br />UI Update<br />onclick<br />time<br />UI Queue<br />onmessage<br />
  127. 127. Worker Thread Complete<br />UI Thread<br />onmessage<br />UI Update<br />UI Update<br />onclick<br />time<br />UI Queue<br />
  128. 128. Repaint and ReflowHidden performance costs of common operations<br />
  129. 129. Long UI updates=Unresponsive UI<br />
  130. 130. Unresponsive UI<br />UI Thread<br />JavaScript<br />UI Update<br />UI Update<br />time<br />
  131. 131. JavaScript can cause long UI updates by triggeringrepaint and reflow<br />
  132. 132. A repaint occurs when a visual change doesn't<br />require recalculation of layout<br />Changes to visibility, colors (text/background), background images, etc.<br />
  133. 133. Repaint<br /><button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button><br /><script type="text/javascript"><br />window.onload = function(){<br /> document.getElementById("btn").onclick = function(){<br /> this.style.color = "#ff0";<br /> };<br />};<br /></script><br />Repaint!<br />
  134. 134. A reflowoccurs when a visual change<br />requires a change in layout<br />Initial page load ▪ browser resize ▪ DOM structure change ▪ layout style change<br />layout information retrieved<br />
  135. 135. Reflow<br /><button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button><br /><script type="text/javascript"><br />window.onload = function(){<br /> document.getElementById("btn").onclick = function(){<br /> var div = document.createElement(“div”);<br /> div.className = “tip”;<br /> div.innerHTML = “You clicked me!”;<br /> document.body.appendChild(div);<br /> };<br />};<br /></script><br />Reflow!<br />
  136. 136. Repaints and reflows are queued up as JavaScript executesand then executed in order<br />
  137. 137. Reflow<br />var list = document.getElementsByClassName("items")[0],<br /> i, item;<br />for (i=0; i < 10; i++){<br /> item = document.createElement("li");<br /> item.innerHTML = "Item #" + i;<br />list.appendChild(item);<br />}<br />Reflow x 10!<br />
  138. 138. Limiting repaints/reflows improves overall performance<br />
  139. 139. Technique #1Perform DOM manipulations off-document<br />
  140. 140. Off-Document Operations<br />Fast because there's no repaint/reflow<br />Techniques:<br />Remove element from the document, make changes, insert back into document<br />Set element's display to “none”, make changes, set display back to default<br />Build up DOM changes on a DocumentFragment then apply all at once<br />
  141. 141. DocumentFragment<br />A document-like object<br />Not visually represented<br />Considered to be owned by the document from which it was created<br />When passed to appendChild(), appends all of its children rather than itself<br />
  142. 142. DocumentFragment<br />var list = document.getElementsByClassName("items")[0],<br /> fragment = document.createDocumentFragment(),<br /> i, item;<br />for (i=0; i < 10; i++){<br /> item = document.createElement("li");<br /> item.innerHTML = "Item #" + i;<br /> fragment.appendChild(item);<br />}<br />list.appendChild(fragment);<br />1 Reflow<br />
  143. 143. Technique #2Group Style Changes<br />
  144. 144. Repaint!<br />Reflow!<br />element.style.color = "red";<br />element.style.height = "100px";<br />element.style.fontSize = "25px";<br />element.style.backgroundColor = "white";<br />Reflow!<br />Repaint!<br />
  145. 145. Grouping Style Changes<br />.active {<br /> color: red;<br /> height: 100px;<br /> fontSize: 25px;<br /> background-color: white;<br />}<br />element.className = "active";<br />Reflow!<br />
  146. 146. Grouping Style Changes<br />var item = document.getElementById("myItem");<br />item.style.cssText = "color:red;height:100px;" +<br /> "font-size:25px;background-color: white");<br />Reflow!<br />
  147. 147. Technique #3Avoid Accidental Reflow<br />
  148. 148. Accidental Reflow<br />element.style.width= "100px";<br />var width = element.offsetWidth;<br />Reflow!<br />
  149. 149. What to do?<br /> Minimize access to layout information<br />offsetTop, offsetLeft, offsetWidth, offsetHeight<br />scrollTop, scrollLeft, scrollWidth, scrollHeight<br />clientTop, clientLeft, clientWidth, clientHeight<br />Most computed styles<br /> If a value is used more than once, store in local variable<br />
  150. 150. Does hardware acceleration help?<br />
  151. 151. Traditional Rendering<br />UI Thread<br />Compositing<br />Drawing<br />time<br />
  152. 152. Hardware Acceleration<br />UI Thread<br />Prep<br />Wait<br />time<br />Rendering info<br />Signal complete<br />Compositing<br />Drawing<br />GPU<br />
  153. 153. Recap<br />
  154. 154. The browser UI thread is responsible for<br />both UI updates and JavaScript execution<br />Only one can happen at a time<br />
  155. 155. Responsive UI<br />UI Thread<br />JavaScript<br />UI Update<br />UI Update<br />time<br />
  156. 156. Unresponsive UI<br />UI Thread<br />JavaScript<br />UI Update<br />UI Update<br />time<br />
  157. 157. Unresponsive UI<br />UI Thread<br />JavaScript<br />UI Update<br />UI Update<br />time<br />
  158. 158. Avoid Slow Loading JavaScript<br />Dynamically created scripts<br />Deferred scripts<br />Asynchronous scripts<br />
  159. 159. Avoid Slow JavaScript<br />Don't allow JavaScript to execute for more than 50ms<br />Break up long JavaScript processes using:<br />Timers<br />Script Yielding (future)<br />Web Workers<br />
  160. 160.
  161. 161.
  162. 162. Etcetera<br />My blog: www.nczonline.net<br />Twitter: @slicknet<br />These Slides: slideshare.net/nzakas<br />Hire us: projects@stubbornella.org<br />
  163. 163. Questions?<br />
  164. 164. Creative Commons Images Used<br />http://www.flickr.com/photos/lrargerich/3115367361/<br />http://www.flickr.com/photos/hippie/2406411610/<br />http://www.flickr.com/photos/55733754@N00/3325000738/<br />http://www.flickr.com/photos/eurleif/255241547/<br />http://www.flickr.com/photos/off_the_wall/3444915939/<br />http://www.flickr.com/photos/wwarby/3296379139/<br />http://www.flickr.com/photos/derekgavey/4358797365/<br />http://www.flickr.com/photos/mulad/286641998/<br />http://www.flickr.com/photos/torley/2361164281/<br />http://www.flickr.com/photos/ottoman42/455242/<br />http://www.flickr.com/photos/goincase/3843348908/<br />

×