Yahoo! Homepage Yahoo! Homepage Nicholas C. Zakas Nicholas C. - - PowerPoint PPT Presentation

yahoo homepage yahoo homepage
SMART_READER_LITE
LIVE PREVIEW

Yahoo! Homepage Yahoo! Homepage Nicholas C. Zakas Nicholas C. - - PowerPoint PPT Presentation

flickr.com/photos/eyesplash/4268550236/ Performance on the Performance on the Yahoo! Homepage Yahoo! Homepage Nicholas C. Zakas Nicholas C. Zakas Principal Front End Engineer, Yahoo! Principal Front End Engineer, Yahoo! Velocity, June 24


slide-1
SLIDE 1

Performance on the Performance on the

Yahoo! Homepage Yahoo! Homepage

Nicholas C. Zakas Nicholas C. Zakas Principal Front End Engineer, Yahoo! Principal Front End Engineer, Yahoo! Velocity, June 24 2010 Velocity, June 24 2010

flickr.com/photos/eyesplash/4268550236/

slide-2
SLIDE 2
slide-3
SLIDE 3

Principal Front End Engineer

slide-4
SLIDE 4

Contributor

slide-5
SLIDE 5

Author

slide-6
SLIDE 6

The Challenge: Create a new Yahoo! homepage*

*add a ton of new functionality **without sacrificing performance

slide-7
SLIDE 7

By the Numbers

345

million

unique users per month worldwide

110

million

unique users per month in United States (no pressure)

slide-8
SLIDE 8

The Legacy

slide-9
SLIDE 9

1996

slide-10
SLIDE 10

1997

slide-11
SLIDE 11

1999

slide-12
SLIDE 12

2002

slide-13
SLIDE 13

2004

slide-14
SLIDE 14

2006

slide-15
SLIDE 15

Today

slide-16
SLIDE 16

We strapped ourselves in, believing we could make the fastest Yahoo! homepage yet

flickr.com/photos/yodelanecdotal/3620023763/

slide-17
SLIDE 17

Performance is hard

The best features for users aren't always the fastest

flickr.com/photos/thetruthabout/2831498922/

slide-18
SLIDE 18

Content Optimization Engine determines which stories to display at request time

slide-19
SLIDE 19

Sites can be completely customized by the user

slide-20
SLIDE 20

Popular search topics are determined at request time to display up-to-date info

slide-21
SLIDE 21

Random information about

  • ther parts of the Yahoo!

network

slide-22
SLIDE 22

Apps provide more info

  • n-demand
slide-23
SLIDE 23

The Cost of Customization

  • Spriting is difficult

– Hard to know which images will be on the page together

  • Limited image caching

– With content constantly changing, getting images into cache doesn't help much

  • A lot more JavaScript/CSS

– And very different, depending on how the user has customized the page

slide-24
SLIDE 24

Performance reboot

Many of the optimizations made on the previous homepage won't work

flickr.com/photos/thetorpedodog/458336570/

slide-25
SLIDE 25

Coming to peace with reality

We can't optimize everything – so let's just focus on the parts we can

flickr.com/photos/hape_gera/2123257808/

slide-26
SLIDE 26

flickr.com/photos/hape_gera/2123257808/

Areas of Focus

  • Time to interactivity
  • Ajax Responsiveness
  • Perceived performance
slide-27
SLIDE 27

The time to interactivity is the time between the initial page request and when the user can complete an action

slide-28
SLIDE 28

Time to Interactivity

  • For most pages, happens between

DOMContentLoaded and onload – Can actually happen earlier

  • Links work, forms can be submitted even while

the page is loading – As long as JavaScript isn't running

  • Difficult to measure
slide-29
SLIDE 29

Net tab reveals some information

Where DOMContentLoaded and onload occur

slide-30
SLIDE 30

YSlow reports onload time

Useful, but doesn't really determine time to interactivity

slide-31
SLIDE 31

Goal: Ensure interactivity by DOMContentLoaded

slide-32
SLIDE 32

Simple User Actions

  • Clicking a headline to read the story
  • Performing a search
  • Clicking on a favorite

Wait a second! You don't need JavaScript for any of that!

flickr.com/photos/marcoarment/2035853550/

slide-33
SLIDE 33

Progressive Enhancement FTW!

The more tasks that don't require JavaScript, the faster the user can complete an action

alistapart.com/articles/understandingprogressiveenhancement

slide-34
SLIDE 34

The page is very functional even without JavaScript

slide-35
SLIDE 35

Not relying on JavaScript for everything allows us an

  • pportunity to deliver what

appears to be a faster experience

slide-36
SLIDE 36

JavaScript

Loading onto the page without pain

slide-37
SLIDE 37

Traditional thinking was put scripts at the bottom

slide-38
SLIDE 38

<html> <head> <!-- head contents --> </head> <body> <!-- body contents --> <script type="text/javascript" src="yourfile.js"> </script> <script type="text/javascript" src="yourfile2.js"> </script> </body> </html>

slide-39
SLIDE 39

Our results were upsetting

Putting scripts at the bottom actually caused other problems

flickr.com/photos/kartik_m/2724121901/

slide-40
SLIDE 40

flickr.com/photos/kartik_m/2724121901/

Results

  • Page would fully render, but be frozen

– User can't interact while JavaScript is being fetched/parsed/executed

  • Delayed onload to after 5s on fast connection
  • Time to interactivity tied to onload
  • Experience was especially bad over slower

connections – Page was unresponsive for 30s or more

slide-41
SLIDE 41

In order to fix things, we had to get lazy

flickr.com/photos/19613690@N05/3687563687/

slide-42
SLIDE 42

stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/

slide-43
SLIDE 43

nczonline.net/blog/2009/07/28/the-best-way-to-load-external-javascript/

slide-44
SLIDE 44

function loadScript(url, callback){ var script = document.createElement("script") script.type = "text/javascript"; if (script.readyState){ //IE script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; callback(); } }; } else { //Others script.onload = function(){ callback(); }; } script.src = url; document.getElementsByTagName("head")[0].appendChild(script); }

Dynamically loaded scripts don't block page load

slide-45
SLIDE 45

<html> <head> <!-- head contents --> </head> <body> <!-- body contents --> <script type="text/javascript" src="smallfile.js"> </script> <script type="text/javascript"> loadScript(filename, function(){ //initialization }); </script> </body> </html>

slide-46
SLIDE 46

Y.Get.script(YUI.presentation.lazyScriptList, { onSuccess: function() { Y.use("*"); Y.ModulePlatform.init(Y.dali.config, true); }});

slide-47
SLIDE 47

Everything else First script file

slide-48
SLIDE 48

flickr.com/photos/nateandmiranda/2625399653/

Results

  • Page is interactive as

soon as each section is rendered

  • Reduced onload time

to ~2.5s on fast connections

  • Slow connection

experience vastly improved

slide-49
SLIDE 49

JavaScript Loads

  • Small amount on page load
  • Larger amount loaded in non-blocking manner

– Everything necessary for core JavaScript interactivity

  • Ajax responses can specify more JavaScript is

needed to handle the response – True, on-demand loading

slide-50
SLIDE 50

Page Flushing

Getting data out to the browser fast

slide-51
SLIDE 51

Traditional thinking is to flush after <head>

slide-52
SLIDE 52

Flushing after <head> ensures CSS starts to download as quickly as possible But the user still sees a blank screen until the rest of the HTML is rendered to the browser Solution: flush after major sections

  • f the page have been output

flickr.com/photos/conskeptical/354951028/

slide-53
SLIDE 53

<div class="doc"> <div class="hd"> </div> <!-- flushing here does nothing --> <div class=”bd”> </div> </div>

The browser won't render a block-level element inside of <body> until the closing tag has been received

slide-54
SLIDE 54

<div class="hd"> </div> <!-- flushing here causes the head to render --> <div class=”bd”> </div>

Removing page-level wrapper <div> allows the head to render as quickly as possible

slide-55
SLIDE 55

Flush Flush

slide-56
SLIDE 56
slide-57
SLIDE 57
slide-58
SLIDE 58
slide-59
SLIDE 59

(Actually, we flush a bunch of times as the page is output)

Even when the browser can't render yet, it can still start to download other resources such as images

slide-60
SLIDE 60

flickr.com/photos/hape_gera/2123257808/

Areas of Focus

  • Time to interactivity
  • Ajax Responsiveness
  • Perceived performance
slide-61
SLIDE 61

The biggest area of concern regarding Ajax performance was around the apps. For our very first test, it sometimes took as long as 7 seconds to load a single app.

slide-62
SLIDE 62

What exactly is taking 7 seconds?

The measurement itself was a huge black box – before doing anything, we had to figure out exactly what was happening in that time

start stop

slide-63
SLIDE 63

Roundtrip Time

The first step is the amount of time between when the browser sends the request and the time it receives the response

start stop

roundtrip

slide-64
SLIDE 64

Parse Time

Next, the JSON response returned from the server has to be parsed

start stop

roundtrip parse

slide-65
SLIDE 65

JavaScript/CSS Download Time

Each response can indicate it needs more JavaScript/CSS before the content can be used

start stop

roundtrip parse download

slide-66
SLIDE 66

Render Time

The amount of time it takes to actually change the display via innerHTML

start stop

roundtrip parse download render

slide-67
SLIDE 67

Where We Started

slide-68
SLIDE 68

Fixing Roundtrip Time

What's taking so damn long?

slide-69
SLIDE 69

The right-side ads were a roundtrip issue

The server-side ad call took extra time plus the ad markup represented 50-60% of the returned markup

slide-70
SLIDE 70

“Fixing” the ad

Entire right column now renders in an iframe. This defers the ad call until after the app has been loaded in the browser, saving both server time for app rendering and bytes in the response.

slide-71
SLIDE 71

Fixing Parse Time

What's taking so freakin' long??

slide-72
SLIDE 72
slide-73
SLIDE 73

{ "msg": "Hello world!", "day": 6, "found": true, }

JSON is super-efficient for transporting numbers, Booleans, simple strings, objects, and arrays

slide-74
SLIDE 74

{ "html":"<div id=\"default-p_26583360\" class=\"mod view_default\"> <div id=\"default-p_26583360-bd\" class=\"bd type_pacontainer type_pacontainer_default\"><div class=\" pa-app-col1 y-pa-ln-open-dk \"><div class=\"hd pa-app-hd\"><h2 class=\"x-large\"><a href=\"_ylt=ArPtckll5ytFOZy32_Tg07qbvZx4\/SIG=10u61l0b2\/**http %3A\/\/finance.yahoo.com\/\">Finance<\/a><\/h2> \t<div class=\"pa- menu-options\">\n \t\t<a role=\"button\" class=\"pa-menu-optionsbtn small pa-app-header\" href=\"#\" data- b=\"_ylt=AhzOmRGiUKjiPuGRaW8LrGabvZx4\">Options<span class=\"y-fp-pg- controls arrow\"><\/span><\/a>\n\t\t\t\t<ul id=\"p_26583360-settings-menu\" class=\"y-menu medium\">\n\t\t\t\t\t\n\t\t\t\t\t<li><a href=\"_ylt=AtqN.M70D5mHiPrcLvHF9vibvZx4\/SIG=1254msah3\/**http %3A\/\/help.yahoo.com\/l\/us\/yahoo\/homepage\/homepage\/myapps\/stocks\" class=\"y-link-1 help-option\"><span class=\"y-fp-pg- controls\"><\/span>Help<\/a><\/li>\n\t\t\t\t\t\n\t\t\t <\/ul>\n\t\t <\/div><\/div><div id=\"default-u_93109\" class=\"mod view_default\"> <div id=\"default-u_93109-bd\" class=\"bd type_finance type_finance_default\"> <div class=\"finance-nav clearfix\">\n <a href=\"_ylt=AvKZuIwh_mvmWInFE6c7Zc.bvZx4\/SIG=10u61l0b2\/**http %3A\/\/finance.yahoo.com\/\" class=\"small text-color goto-link\"><span class=\"goto\">Go to:<\/span> <span class=\"property\"><img src=\"http:\/\/d.yimg.com\/a\/i\/ww\/met\/mod\/ybang_22_061509.png\" alt=\"Finance\"> Finance<\/span><\/a>\n <ul class=\"y-tablist med-small\" id=\"u_93109-tabs\"> <li class=\"selected\"><a href=\"#\" data- b=\"_ylt=AhW8HKKgyZxBNcux07hCVxGbvZx4\">Overview<\/a><\/li> <li class=\"\"><a href=\"#\" data-b=\"_ylt=AuEzZyDTq.4N_vTGBXpu2VubvZx4\">My Portfolios<\/a><\/li> <\/ul>\n <\/div>\n <div class=\"y-tabpanels y-tp- default\">\n <div class=\"tabpanel selected\">\n <div class=\"menu menu- empty y-glbl-mod-grad\"><\/div>\n <div class=\"market-overview\">\n <div class=\"holder\">\n <p class=\"x-small date text-color\">Sat, Jun 12, 2010 10:10am PDT<\/p>\n <table class=\"index-update\">\n <tbody>\n <tr class=\"med-large\">\n <td class=\"hide-contents hide- textindent\">&nbsp;<\/td>\n" }

Very inefficient for large HTML strings

Escapement adds a lot of extra bytes

slide-75
SLIDE 75

The larger the JSON string, the longer it took to parse

Keep in mind there was no native browser JSON parsing when we began testing the new page The regular expression validation in the YUI JSON utility (based on json2.js) could take up to 40% of the parse time

slide-76
SLIDE 76

Shrinking the size of the HTML by deferring the ad helped

But we still wanted to see if we could eek out better gains

slide-77
SLIDE 77

[{ "msg": "Hello world!", "day": 6, "found": true, }] ===== <div class="foo"><a href="http://www.yahoo.com">Yahoo! </a></div>

We experimented with an alternate response format where meta data was in JSON form but HTML was included afterward in plain text

slide-78
SLIDE 78

Results were good

But then native JSON parsing hit and a lot of problems went away

flickr.com/photos/mattymatt/3017263513/

slide-79
SLIDE 79

Fixing Download Time

What's taking so (*&#$Q@! long???

slide-80
SLIDE 80

On-demand JavaScript/CSS downloading hurt app loading time

Intended to decrease page load time, and did – but left us with this side effect

slide-81
SLIDE 81

Waiting until the user takes an action ensures paying the cost of download What if you knew which apps the user was going to use? Solution: predictive fetch of JavaScript/CSS before you need it

flickr.com/photos/mcgraths/3248483447/

slide-82
SLIDE 82

After page load, we start to download JavaScript/CSS for the apps on the page

slide-83
SLIDE 83

After onload

  • nload
slide-84
SLIDE 84

Fixing Render Time

WTF is taking so (*&#$Q@! long?!?!?

slide-85
SLIDE 85

Actually, render time was okay

slide-86
SLIDE 86

Results

slide-87
SLIDE 87

flickr.com/photos/hape_gera/2123257808/

Areas of Focus

  • Time to interactivity
  • Ajax Responsiveness
  • Perceived performance
slide-88
SLIDE 88

Don't underestimate the power

  • f perception
slide-89
SLIDE 89

Initially, the new page was actually slower to load than the previous

To be expected – a lot more JavaScript and CSS

slide-90
SLIDE 90

Blank space is bad

Makes it seem like nothing is happening

slide-91
SLIDE 91

Adjusting Perception

  • Frequent page flushing

– Progressive rendering avoids a lot of blank space

  • JavaScript at the bottom

– Ensure it doesn't block rendering

  • Lazy load JavaScript

– Decrease time to interactivity

slide-92
SLIDE 92

Initially, apps were completely blank while loading (seemed slow)

slide-93
SLIDE 93

We changed it to a pseudo- loaded state, which made loading seem faster

slide-94
SLIDE 94

In the end, user testing showed that perceived performance of the new page was the same as on the old page

slide-95
SLIDE 95

Wrap Up

slide-96
SLIDE 96

Lessons Learned

  • Time to interactivity is a big deal
  • Progressive enhancement creates a better

experience – Allows for delayed load of JavaScript

  • Load as much JavaScript as possible in a non-

blocking manner

  • Ajax performance is a macro measurement

– Get more insight by looking at the parts

  • Perceived performance is important
slide-97
SLIDE 97

flickr.com/photos/ficken/1813744832/

Achievements

  • Reduced time to onload from ~5s to ~2.5s

– Actually better than previous version

  • Very short time to interactivity
  • Reduced time to open apps from ~7s to ~2s
  • Maintained perception of speed from previous

version

slide-98
SLIDE 98

Questions?

slide-99
SLIDE 99

Etcetera

  • My blog:

www.nczonline.net

  • My email:

nzakas@yahoo-inc.com

  • Twitter:

@slicknet