Web Performance: Myths and Truths
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 96 | |
Author | ||
License | CC Attribution - NonCommercial - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/51857 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
NDC Oslo 201667 / 96
2
7
8
9
12
14
19
20
26
28
31
33
38
40
43
45
48
50
51
61
63
65
76
79
80
83
87
88
90
93
94
96
00:00
World Wide Web ConsortiumRootMathematical optimizationRule of inferenceRevision controlRule of inferenceCombinational logicDifferent (Kate Ryan album)Structural loadMoore's lawWeb pageWebsiteWeb serviceBand matrixWeb browserRootSoftware testingRight angleBlogVirtual machineMobile appWeb applicationInternetworkingEndliche ModelltheorieSoftware developerNumberInstance (computer science)Mathematical optimizationUltraviolet photoelectron spectroscopyArmSeries (mathematics)Server (computing)Replication (computing)Process (computing)Java appletCartesian coordinate systemModal logicScripting languageTwitterInformationAreaCASE <Informatik>LogicComplex (psychology)Line (geometry)NP-hardBookmark (World Wide Web)Client (computing)World Wide Web ConsortiumComputer hardwareMultiplication signFront and back endsRegular graphMobile WebPhysical systemComputer animation
05:05
View (database)Computer iconPhilips CD-iNormal (geometry)Web pageCross-site scriptingHost Identity ProtocolComputer fileComputer-generated imageryGoogolContent delivery networkComputer networkDistribution (mathematics)ComputerReal numberTime domainFluid staticsHTTP cookieContent (media)Domain nameWeb browserRootCache (computing)EmailData compressionRevision controlStructural loadWeb pageComputer fileWeb applicationBookmark (World Wide Web)1 (number)Multiplication signData exchangeIntegrated development environmentStatisticsFile archiverContent (media)CASE <Informatik>Computer iconLibrary (computing)Link (knot theory)Cache (computing)Transport Layer SecurityRule of inferenceOrder (biology)Content delivery networkLatent heatWebsiteConnected spaceReal numberCore dumpHTTP cookieDomain nameMultilaterationState of matterWorld Wide Web ConsortiumOverhead (computing)Web browserFluid staticsWeb serviceVirtual machineArithmetic progressionMereologyMedical imagingServer (computing)2 (number)Mechanism designNumberGraphical user interfaceElectronic mailing listProcess (computing)Application service providerManifoldGoodness of fitBitTwitterInternet service providerDifferent (Kate Ryan album)Connectivity (graph theory)CodeTerm (mathematics)ResultantSlide rulePoint (geometry)MathematicsStrategy gameBand matrixParallel portInformation.NET FrameworkPairwise comparisonRight angleForm (programming)Profil (magazine)TheoryVideo gameInformation retrievalScaling (geometry)Reading (process)Decision theoryGastropod shellVideoconferencingSet (mathematics)View (database)System callArmEndliche ModelltheorieXMLComputer animation
15:02
HTTP cookieDomain nameTime domainFluid staticsContent (media)RootCross-site scriptingWeb browserContent delivery networkCache (computing)Data compressionEmailBit rateWeb pageControl flowComputer-generated imageryComputer fileProbability density functionComponent-based software engineeringClient (computing)Server (computing)Dressing (medical)BuildingFile formatGraphics processing unitAverageFile archiverMathematical optimizationView (database)Sampling (music)Artistic renderingMereologyPresentation of a groupBuildingData compressionCASE <Informatik>Module (mathematics)CoprocessorFile archiverWeb serviceGoodness of fitNumberRegular graphLimit (category theory)Cycle (graph theory)AdditionBefehlsprozessorCache (computing)Connected spaceGame controllerRight angleContent (media)HTTP cookieInternetworkingRule of inferenceGraphical user interfaceRevision controlMathematicsMultiplication signRootDomain nameStructural loadWeb browserBand matrix3 (number)Content delivery networkGraphics processing unitComputer fileOffice suiteMedical imagingMathematical optimizationClient (computing)Server (computing)Pairwise comparisonMarkup languageWorld Wide Web ConsortiumWeb pageEmailFluid staticsLine (geometry)Different (Kate Ryan album)DatabaseAreaTerm (mathematics)Mobile WebProcess (computing)Software testingLink (knot theory)InformationSoftware developerComa BerenicesOrder (biology)Cartesian coordinate systemForm (programming)File formatSuite (music)Overhead (computing)Centralizer and normalizerScaling (geometry)Heat transferConnectivity (graph theory)Mixed realityRoutingState of matterExistenceInformation securityLevel (video gaming)Musical ensembleEndliche ModelltheoriePoint (geometry)Lie groupState observerComputer animation
25:00
Computer-generated imageryMathematical optimizationView (database)Sampling (music)Artistic renderingSheaf (mathematics)Web browserCross-site scriptingComputer fileWeb pageGreatest elementScripting languageLimit (category theory)Client (computing)Sound effectCodeVector potentialJava appletConcurrency (computer science)Regulärer Ausdruck <Textverarbeitung>Exception handlingStructural loadLink (knot theory)Meta elementContent (media)GoogolWebsiteMedical imagingSampling (statistics)Web pageScripting languageCodeExpressionCalculationObservational studyBit rateWeb browserNumberRule of inferenceRight angleJava appletArithmetic meanGroup actionLinear regressionElectronic visual displayComputer configurationSheaf (mathematics)Content (media)BitBlock (periodic table)Information securityGraph coloringTerm (mathematics)Structural loadInstance (computer science)Process (computing)Perspective (visual)Event horizonCuboidSign (mathematics)Physical lawMoment (mathematics)Line (geometry)Binary fileArithmetic progressionRing (mathematics)Form (programming)Source codeInternet service providerPay televisionMultiplication signReading (process)Flash memoryOnline helpDisk read-and-write headGraphical user interfaceConnected spaceWeb applicationTwitterGreatest elementSoftwareComputer iconCASE <Informatik>NewsletterVolumenvisualisierungLatent heatRootServer (computing)World Wide Web ConsortiumMachine codeApplication service providerRevision controlAdditionSoftware testingRegular graphParsingInjektivitätCodeComputer animation
34:58
Parallel portDomain nameRule of inferenceBand matrixWeb pageLink (knot theory)StapeldateiOperations researchMaxima and minimaJava appletScripting languageVariable (mathematics)Web browserCache (computing)Function (mathematics)String (computer science)Integrated development environmentResultantNumberLevel (video gaming)SpacetimeArithmetic progressionPairwise comparisonInformation securityMereologyWorld Wide Web ConsortiumUniform resource locatorSound effectConnected spaceParsingEvent horizonComputer configurationState of matterWeb browserLink (knot theory)Multiplication signCartesian coordinate systemData exchangeWeb pageCache (computing)Home pageRun time (program lifecycle phase)Direct numerical simulationInformationCore dumpDifferent (Kate Ryan album)ThumbnailWebsiteException handlingHTTP cookieInheritance (object-oriented programming)Structural loadScripting languageCodeVariable (mathematics)Rule of inferenceEmailComputer fileDomain nameContent (media)Band matrixDirectory serviceRootSoftware testingFluid staticsString (computer science)Functional (mathematics)Configuration spaceMaxima and minimaOperator (mathematics)FingerprintTable (information)StapeldateiView (database)Revision controlInstance (computer science)2 (number)MathematicsVarianceGUI widgetClient (computing)CASE <Informatik>Template (C++)Server (computing)Medical imagingReduction of orderMultiplicationBitSoftwareConcurrency (computer science)WritingReading (process)Focus (optics)Category of beingArray data structureLoop (music)Goodness of fitDynamical systemWeb serviceRight angleJava appletProcess (computing)Algebraic closureConnectivity (graph theory)Query languageSeries (mathematics)GoogolGroup actionUltraviolet photoelectron spectroscopyLocal ringWordElectronic mailing listArithmetic meanComa BerenicesInternetworkingRing (mathematics)Perfect groupCodeLogical constantRoutingPhysical lawMusical ensembleObservational studyValidity (statistics)MeasurementAnalytic setCausalityTask (computing)Line (geometry)ArmTouchscreenDependent and independent variablesDevice driverLimit (category theory)QuicksortTerm (mathematics)Intrusion detection systemDomain nameFreewareGastropod shellParsingMathematical optimizationForm (programming)Computer animation
Transcript: English(auto-generated)
00:04
All right. Good morning. I love the session right after the big party. Because you kind of have to work twice as hard, but it's at least twice as fun. Thanks for showing up at this kind of early morning hour. And I promise you we'll have, well, 50, 55
00:24
minutes of, well, quite some interesting information on one of my most favorite topics, web performance. So I've always been kind of a performance guy and I've been doing web performance for quite some time as well. And with web performance there are many
00:44
interesting aspects. There are aspects with regards to the server, but in my opinion front-end performance is the area where you can kind of do the most and where you have to do the most. Because, you know, in the old days the
01:01
server was under very heavy load and the client was idle most of the time. But with modern web applications with a lot of JavaScript logic and stuff going on in the browser and complex things browsers and clients have to do and mobile browsers on hardware that's maybe weaker than, you know, regular desktop system hardware, a lot of stuff is going on in the browser in the
01:24
client. So you have to work on the performance there. That's what I'll be talking about. I mean, I'm a very proud recipient of one of these Donald dude's reward checks, so I always have to find an excuse to kind of reference him and the way I'm doing it at this NDC is that I just mentioned his
01:43
one of his most favorite quotes actually, that premature optimization is the root of all evil. Well, I mean, of course it makes sense at least quite often that you should not over optimize before it's actually necessary
02:02
that you do optimize, but you need to find out when it's necessary. On the other hand, when we talk about web applications, with web applications there are some kind of guidelines for performance that absolutely make sense no matter if there is a need for optimization because you just gain a lot
02:22
out of using them. Even if you have a web application that is not under heavy load, you do benefit from following a couple of rules. Well, there are a couple of rules and the person that kind of, well, actually started a web performance trend is Steve Souders who once worked for
02:43
Yahoo on the web performance team then went to Google and then went to a couple of startups and he wrote two books for O'Reilly and in the first book, which kind of evolved out of a series of blog posts, he set up 14 rules for faster loading websites. That was 2007, nine years ago. There was a second
03:05
book coming two years later, but still this is old and I thought okay, wouldn't it be interesting to kind of have a look at those rules again and see if they are still valid because I mean many of those
03:21
rules they are really common sense, but many are very clever and specific to the needs of web applications. However, things evolved. Moore's law, right? So hardware got better and faster. We've got more bandwidth today. We didn't have say LTE back in the days, right? On the other end, browsers got better.
03:42
Browsers are now brutally optimizing the performance of web applications. That wasn't a trend back then. I mean, just remember how fast Internet Explorer 6 was, right? But nowadays, especially Chrome, but also the other browsers, also the Edge browser, they really try to improve the performance a lot, which
04:03
might mean that some of the advice that was valid back then and is still repeated everywhere on the web might not hold today. Also, there are new developments coming, probably most prominently HTTP version 2, which also changes some of those rules. So what I'd like to do this morning is we have
04:23
a look at those 14 rules from 2007. I'll briefly explain them and then we see is that still true? Is that advice still true or is it a myth? And that's that's what I want to do today. Okay, so let's get started and rule number
04:41
one was make fewer HTTP requests. And the general consensus or idea here was well, the less HTTP requests you make, the better it is. How can you reduce the number of HTTP requests? So for instance, you can combine files. I actually ran webpagetest.org on a couple of pages. So probably no
05:08
webpagetest.org, right? So it's a website or a web service where you can get your website checked. So they have virtual machines all over the world with different browser versions. So you can say, okay, go to a service in Washington
05:24
DC, load the NDC Oslo page and then show me a chart how that loading worked. Actually, I did this last week and what happened is nothing. And I really thought, okay, this maybe a service doesn't work. So I tried it out on a VM myself with IE8. Ah, the NDC Oslo page isn't supported by IE8, right? So
05:45
actually you get redirected to not support it. Okay, so probably I was the first person ever trying to load the page with IE8. Okay, so I learned from that. So I took a more modern browser and second attempt. And so this is Google Chrome, right? And you see a lot of stuff is being loaded and
06:03
especially towards the top, you see that there are five, six JavaScript files and three fonts and then, well, a few more JavaScript files are being loaded. So a typical approach here is, as you all know, to combine those files, right?
06:23
The different ways of doing that, but with the progression of CI environments, probably the easiest thing really is that you kind of combine those files as part of the DCI process. In ASP.NET we have bundling mechanism, other server technologies have bundling mechanisms. So that's
06:42
the general approach because HTTP requests, well, they kind of do take some time, right? We see here that Chrome kind of has six connections to the same server at the same time, right? Which is progress because back then when the rules were made, well, oh no, let me put it another
07:04
way. The HTTP specification says we can only have two concurrent connections to the same server at the same time. Well, back when the HTTP specification was written, hint, last century. And thus this was something browsers
07:22
eventually started to ignore when bandwidth got better, right? But still only six connections, so all those six connections are here kind of wasted on loading JavaScript, so it might be better to just load one JavaScript file, which then of course is larger, but on the other hand we might be able to load something else in parallel. JavaScript is a different beast because depending on
07:42
the browser and on the version, JavaScript code is loaded in a blocking fashion because JavaScript code has the guarantee of the order of execution. So if you reference three JavaScript files in your HTML, those
08:00
JavaScript files are loaded and even if they are loaded parallely, the execution order is the same as you have in HTML. So you have a guarantee if you first load, let's say, AngularJS and then you load a JavaScript file where you kind of set up your components or your modules, then first the core library of Angular is executed and then your code is executed. Otherwise we would really
08:24
have timing issues on web applications. But generally, so that's an approach that did make sense. Also another aspect is, oh that looks weird, okay, better. Another aspect is using Caesar sprites.
08:44
Now Caesar sprites is a technique I guess you're all aware with. The idea is here an HTTP request is pricey, right? So we have kind of the handshake and then eventually we exchange data because that's the way HTTP was designed back then when HTTP was only meant for, you know, scientists uploading their
09:03
scientific papers and well we have links in HTML, so linking them. But that was the use case back then and that's what HTTP was optimized for. And the idea here is, okay, instead of loading like 20 tiny images which uses icons all over our page, we just put them together in one big image, we load that
09:22
image once and then we crop it, right? So the same big image is then shown on the page 20 times but always a small part of it. That's image sprites, right? So we have one big file but that file is only loaded once. So that's the general idea. One of my favorite kind of performance statistics pages is the interesting dot PHP page of the HTTP Archive and they
09:47
show a lot of interesting data. But that one is kind of fascinating, at least in terms of performance tip number one. It shows the number of requests per page and you see that only 14% of pages as of now, or websites as of now,
10:03
web pages as of now, have less than 26 requests. And so you see that approximately, let me do the math, about 50% have over 100 requests. So a lot of stuff. And remember, only six at the time, so it takes a while to load the
10:22
page. So reducing the number of HTTP requests is generally a good idea. At least that's what we think. But on the other hand, even though that's principally still true, it will change with HTTP too. Why? Very interesting book on
10:45
HTTP too. You can read it online for free, so I put you the direct link here and you get the slides afterwards and the sessions also videotaped. And what they specifically recommend is that all of these workarounds we do for
11:00
HTTP 1.0 and 1.1, including concatenating files, including using image sprites, shall be removed once we have HTTP too. Because we have parallel downloads then. We have only one TCP connection to a server and then data
11:21
exchange. So we can kind of remove the craft. I am not completely convinced by the recommendation to avoid concatenated files, because I do think it makes sense here. Because I mean, what happens between having individual files in our development environment and then what the end result of CI is, shouldn't
11:46
really matter in the end. But of course in general, once HTTP 2.0 is widespread, and I mean browser port of HTTP 2.0 is pretty good, web server support is technically okay. So all modern versions kind of do it. But well, it
12:02
still has to be activated. And once you go to HTTP 2.0, you have to have a strategy for all browsers, of course. And B, you might need to revamp your website from a performance point of view, because of the changes that come with HTTP 2.0. So in general, this rule make few HTTP requests still holds true. But with HTTP 2.0, that will change a bit. Tip number two, and I was surprised back then in
12:25
2007 that that was number two of a list of 14, use a CDN. Because I thought, you know, CDNs are a good idea in general, but if you have a website that doesn't have a global scale, and maybe it's in a smaller country, then why
12:40
should you distribute that if almost all visitors are from kind of same place, and would get a CDN server, well, nearby, which probably is the only CDN server in the country, right? And the idea is here a manifold. So one is, if you're using one of those standard libraries, let's just say AngularJS,
13:01
jq react, then you might not be the only one using that library. The idea is if you load it with a CDN URL, which then is, you know, automatically distributed worldwide, and you get it from the closest server, then that library might already be in your browser cache. And we'll talk about caching in a bit, because there's also a rule about caching, two rules actually.
13:23
So that's where CDN might come in handy. And even if you have a small website, and if you have a site with a global or more global audience, of course that absolutely makes sense. And still, that's still true, that CDNs are very, very effective. And well, the trend since a couple of years
13:45
is not using, you know, theory, but RUM, so real user monitoring, and then really try to find out, maybe even in the browser live, which CDN has the best performance. So you don't even have to trust what your CDN provider is telling
14:01
you, but can really find out, okay, this takes that long, this takes that long, so let's use that CDN. But there's one kind of catch for the CDN, or one concern that you read kind of often. And that has to do with the domains you are retrieving static content from, right? So one of those
14:24
advices, and we will come back to that again a bit later today, is if you have static content, you might want to load that static content from a cookie-less domain. Why a cookie-less domain? Well, because for static content
14:40
you need any state information, right? But if you have two kilobytes of cookies from your main domain, then that of course means when you load static content from that main domain as well, all your cookies are being sent back to that domain, and it's just not needed, but you have an overhead, you have a big
15:01
overhead. So Microsoft CDN at first was rather slow in comparison to the competition, and then they found out, oh wait a minute, all the Microsoft.com cookies are being sent to that CDN, because it was running under the Microsoft.com domain. So they changed it to a different domain, and
15:22
suddenly the CDN was speed-wise up on par with the competition, right? So generally it's a good idea. However, if you have a modern browser, you should really think about at least serving the main CSS from your root domain. And we can see that, it's on the slides as well, but we can see that
15:40
here too, if you have a look at here, at the top portion, you see that we connect, so that's Chrome, right? We connect to ndcoslo.com, and at the same time the browser opens a second connection to ndcoslo.com, and we first load the
16:00
HTML markup of ndcoslo, and then it loads the main CSS file. But the connection is already opened here. Wait a minute, why is it opening the connection that early, when we haven't even started loading the HTML yet, right? So we connect to ndcoslo.com, that's the yellow bar, but at the same time
16:21
Chrome opens a second connection. Why? Because Chrome assumes that in the HTML the first thing that's referenced externally will be the CSS, and chances are that this CSS comes from the same domain. So the connection is already open, right? So that's one of these tricks by Chrome that sometimes is so
16:45
fast, it opens a second connection just by, just, you know, hoping, just inside, right? Just hoping that there will be another request to that domain later on. And so that's why we can start loading the CSS right here, after the HTML has been loaded, and not, you know, have an orange bar here. So that's
17:05
a very effective technique that Chrome is doing, and that's why, here I was, sorry, and that's why you should serve your CSS, your main CSS from your
17:24
domain, because the connection is already open, and I guess other browsers will follow suite. You could also consider using a CDN for the root domain, which might facilitate caching. Once we have HTTP2, it gets even better, because there is, of course, a downside serving the CSS from the root domain, because all
17:44
cookies are being sent there, right? So we have an overhead. And, of course, if you have more than, say, one and a half kilobyte of cookie data, then this does add some burden to the HTTP request, and, you know, the HTTP request is uploaded and with asynchronous internet connection that just, you know,
18:03
is not as efficient as download, most of the time. But HTTP2, among other things, can compress headers, so situation will get better there as well. Alright, number three. Add an expires or a cache control header. Okay,
18:21
so what does that mean? Well, we can instruct the browser to cache content, and there are several approaches of doing that, and the expires header says, okay, this resource has an expiry date of this and that, and once this expiry date has been met, the browser needs to get a new copy once
18:44
the resource is loaded, but before that, the browser just doesn't try, right? And with cache control, you can also say, will this content be cached or will not be cached? Well, you think about caching, I mean, as a developer, caching always is a bad thing, because it kind of might irritate us, so we see an old
19:02
version, or our customers see an old version of the application, but there's already a new one, but of course generally performance-wise, yes, yes, yes, please, please do caching. So that still holds true. So do use caching. There will be a change to that, but we'll see that in a while. Next piece of advice is to gzip components. That was performance rule
19:24
number 4 out of 14, and the idea is we have content negotiation in HTTP, and one of those aspects is that almost all browsers today send the accept encoding HTTP header as part of a request, telling the server, oh, by the way, if
19:41
you sent me gzipped data, I would be fine with that, and that is a very good technique, because gzip is pretty effective, especially for static content like HTML or CSS or JavaScript, where we have a limited charset, right, so we have
20:00
letters and a few special characters, but basically that's it, and anything that's not already compressed, what's already compressed, in many cases PDF, in most cases images, Office document actually are zip files nowadays as well, at least with most formats. It's a good idea to do caching that, and
20:25
then the good thing is the browser sends that header automatically, so on the server you can just determine, okay, is that header present, and if so, we send the gzip version, and if not, we just send a regular version, but, well, HTTP 1.0 or clients that do not support gzipping, they're extremely rare.
20:44
However, there are even better techniques nowadays, so you might even consider why compressing data on the fly, on the server, because that requires additional CPU cycles. You could compress your data as part of your build process, as part of your CI process, and so, for instance, if you use Nginx as the web
21:06
server, there's a module called gzip static, and the idea is, if you have, say, angular.js, and you have angular.js.gz present in the same folder, if that module is activated, and the client requests angular.js, and supports gzip
21:23
encoding, then what the server does is, it sends back the already gzipped files. That's actually a pretty good idea, you always have to remember that especially mobile devices have a processor that's maybe not as good as a regular one, and thus the decompression takes some time, right, so it puts some
21:46
effort on the client, but still, you just transfer less data, and that's always a good thing, and boosts our performance. A special case is images. So on that HTTP archive page I've just put up, you see that the current
22:01
average page on the web has over 2 megabytes of data, which I think is a lot, and about 1.5 megabytes is image data, so that's the average, right, so there are outliers. Now, an image is kind of compressed, right, so you save in JPEG, and then you set the quality level, or PNGs are automatically
22:21
compressed, but still, there are services that can make the compression even better by removing information the eye might not see, by reducing the palettes, stuff like that. So there is something for PNGs, there's something for JPEGs, different services, really, really good to use, and you can also use them
22:40
as part of the CI process, you don't have to do anything, they are kind of calculated and optimized while you go without any extra effort required. This is, in my book, in terms of bandwidth conservance, the best approach you can take, right, because you don't have to do anything, you get images, but at the
23:01
end the images kind of look the same, but you saved, on average, I would say 25% of the bandwidth. There are also new formats, so WebP, or SubFlee, and others, WebP probably is the most famous one, and they are also very good, they are really optimized for the web, they offer great compression, but you
23:21
have to think about one thing, so JPEG, PNG, they are really old, right, so these formats, they exist since over a decade, even longer, seven decades, so processors are optimized, especially GPUs are optimized for decompressing the data. For those new formats, there is no optimization baked in, right, so it puts
23:45
extra effort on the CPU or the GPU, to be exact, especially on mobile devices, so if you're trying one of these cool new formats, do test with older legacy mobile devices, because they probably will have most issues with them
24:02
performance-wise, because they are not optimized, or the process is not optimized for that format, so that's pretty important to do. There is much more to say about images, just a few things, and you see that I put a lot of links and references in those slides, so that you have the databases for that, and can read further in those areas that are of most interest for you, and
24:24
one aspect is that if you have different devices of different sizes, then of course you can have, you know, one master image, and that master image is sent to all devices, and they then scale that, but of course that's not efficient, because you transfer more data to those clients than they need, so
24:41
you should resize images before that, and then send them out to devices. Also there are different ways of structuring the images, so that part of the image is shown while it's loading, so one is that the image is loaded line by line, right, and one is where the image is just shown in a kind of
25:00
blurred fashion, and then, you know, it gets more and more detailed, and they did some interesting studies, user experience-wise, pretty interesting, so please, please do read that article, very, very interesting, because they were measuring the heart rate of users, and stuff like that, very fascinating, and progressive image rendering, which has been recommended for a couple years,
25:23
somehow leads to users not being as happy with the site, than using, say, a regular image format, because they have to focus more, it puts more strain on the eye, because, you know, it's first blurry, and then you think, oh my, my, my, my eyesight is getting worse and worse, and it's getting better and better, so it requires more focus, so you should use a regular format, if possible, and
25:47
there's also something rather new, chroma sub-sampling, and so the idea is, if colors changes a little bit, we don't notice it, but if Lumity is changing a little bit, we do notice it, so changing the color is much better, because, again, it
26:02
doesn't put that much strain on the eye. Also, we have two terms, above the fold and below the fold, above the fold is what you see when the page loads, below the fold is what you see when you're scrolling, and what, for instance, MSN did, when they reworked their site from a performance perspective, is all images below the fold are lazy loaded, so kind of when people start
26:23
scrolling, or when the page has been fully loaded so far, those images are loaded. Sometimes, if you are impatient like I am, and you start scrolling like crazy when the page is about to load, then this is kind of cumbersome, right, so you scroll, and then, oh, there's nothing, oh, there's a loading sign, I hate this page, but on the other hand, it's very, very effective, what you see
26:43
above the fold needs to be there as quickly as possible, what you see below the fold needs to be there when you scroll below the fold. Okay, number five, put style sheets at the top. Why should you put style sheets at the top? Well, obviously, the browser waits a little bit until he styles the page, and if the
27:05
style sheet is loaded rather late, well, the browser, you know, just waits and waits and doesn't start to render, but we want the browser to start to render as soon as possible, so the users see something, and the perceived performance is great. When users see, oh, it starts rendering, the user automatically
27:23
thinks, okay, page is there, almost done. If nothing happens, the page stays blank, not so good, and if you're on a slow network, like a hotel network, what sometimes happens is, so the HTML is loaded, but the CSS hasn't been loaded yet, and then the browser gets desperate and renders the HTML without any styling, so
27:41
it looks like 1994 websites, and then suddenly the CSS is there, and yeah, then then suddenly the page kind of flashes, and it's colorful, which is not such a good idea. So put style sheets at the top, still a great idea, and remember the web page test chart we just saw, Chrome opens a second
28:03
connection to the root server, so if CSS is loaded first in the head section, then the connection is already open, so great performance there. So that rule is still true, and what you should do, and for instance there's a new feature in the upcoming ASP.NET MVC version that does that, which I think many of you
28:22
are using or will be using, is you can flush in the middle of the page. There's a new help for that, and of course flushing makes sense, right? Because, you know, sometimes the server waits until he sends a chunk of data to the client, but if you explicitly flush after, let's say you have referenced the CSS or the head section, then the browser already gets data and can do
28:44
its work. So still true. The second tip was one, the sixth tip, sorry, rule number six was one that kind of surprised me when I first read it, and I had mixed feelings about that. Turns out, and that's really rare, turns out inside I was right. Put scripts at the bottom. That didn't sound intuitive to me,
29:06
but of course there was some sense behind that, because JavaScript is loaded in a blocking fashion, at least most of the time. I mentioned that before, right? So if JavaScript is loading the head section, like I learned it in
29:21
1995, then the JavaScript is loaded, and during that time the browser does nothing else, doesn't load anything, doesn't render anything, because JavaScript has preference. Well, that's, so putting JavaScript at the bottom is okay, right? So it's loaded last, so the page is first shown and rendered, so
29:43
users think, wow, that's a fast web application, and so I'm using a specific webmail provider for, you know, newsletter subscriptions and other spam. I go there, they load JavaScript very late, so you see kind your inbox, and see, oh,
30:01
you got 20 new email, all spam, then you start, you know, clicking on the checkboxes, marking those emails, and then you want to click on the trash bin icon. The problem is, when loading the page initially, the trash bin icon is deactivated, and when you click on a checkbox, a JavaScript event handler
30:22
activates that trash bin icon. The problem is, that code that sets up this event handler is loaded very, very late, so I've been marking 20 checkboxes, trash bin still deactivated. That makes me angry, and so I thought, okay, maybe that's not always the best idea, and the current
30:43
consensus in the web performance community is, ignore that rule, or kinda ignore that rule. Why? There are two kinds of JavaScript codes. The code you need immediately, and the code we might need later. The code that we need immediately needs to go in the head section, because then it's loaded
31:04
immediately, then it's available as soon as possible. Now, what about the code we do not need at the moment? We might need later on the page, tracking, stuff like that. Well, there are ways to load code asynchronously. Almost all browsers
31:20
support that. Beautiful graphic here by Peter Beverlow, and we have, you know, regular script, so script is loaded, is executed, and then the HTML parser, which is the green line, just continues its thing. If you do script.defer, the page is, you know, loaded asynchronously while the parser is still doing its work,
31:43
and once the parser has done its thing, then the JavaScript code is executed. ScriptAsync, on the other hand, loads in an asynchronous fashion, and once the code has been loaded, it gets executed immediately, right? So these are the options we have, and of course that makes it everything much, much more
32:02
efficient. Also, once we have HTTP2, which can load content in an asynchronous fashion automatically, this will change again a little bit, but basically the code you need now loads immediately, the code you do need later just loaded with scriptAsync or scriptDefer, but there are no reasons for loading everything at
32:23
once, there's no reason for loading everything at the bottom, because at the bottom just means, well, it's executed late, and it kind of doesn't block, but we have two options for loading code that doesn't block, and we have one option for loading code that needs to run immediately, so that's what still is the recommendation we have, right?
32:44
Number seven, oh well, avoid CSS expressions. I mean, I really had, I knew what CSS expressions were, because if you've been to my security talk yesterday, you can use CSS expressions to kind of inject JavaScript code, but apart from attackers, no one is really using CSS
33:02
expressions. CSS expressions were an ingenious invention that allowed us to execute or to do calculations within CSS. I know there's a kind of new approach in CSS3, but the problem with that is, CSS expressions are costly, because these calculations always have to take place when there's a kind of a repaint
33:22
of the page, so basically it's something to be avoided. It was something to be avoided back then, it is still something to be avoided, browser port is very limited. I don't know why this is number seven on the rule set, I didn't understand it in 07, I don't understand it now, so yeah, let's just ditch this
33:41
rule. Number eight, rule number eight, make JavaScript and CSS external. Why? Because if they're external, resources can be cached. Okay, but that does make sense, right? I mean, let's have a look at a big website, so
34:03
how about, so here's the NDC hostel site, let's go to Google. Okay, and that's a Google search, and then we have a look at the source code, and oh wow, that's just brilliant. Almost no external HTTP request takes place, or additional HTTP
34:25
request takes place when you load the Google website. Why? Well, they kind of reference the image, so in that case, I mean, you know, tonight is the start of the euros, so that's why they have the doodle of the day. They do load the
34:40
icon for the browser, but apart from that, all the JavaScript code is in-line, all the CSS, which is somewhere here later, is in-line. Well, there will eventually be CSS. Oh yeah, looking good, right? It ran through a kind
35:02
of parser, so that they were using expressions, terms, identifiers, that are as short as possible. That's compact, because Google tries to optimize the experience of loading the Google application. Google is a very complex
35:21
front-end application. I mean, it's even more complex on the server, right? But on a front-end, it's really complex, because you start searching, you already see results, stuff like that. A lot is going on, a lot of JavaScript code is required for that, but they put it all in here, so that the page just is there as quickly as possible. Now, in theory, with HTTP2, they could revert to
35:46
external files, but again, they are optimizing so so crazy, that actually for them, it does make a lot of sense. And indeed, that rule, make JavaScript and CSS external, is still true, because, I mean, Google is kind of
36:01
the one exception that kind of confirms the rule, okay? So that one is still true. All right, making good progress, so let's go to rule number nine, reduce DNS lookups. The idea here is a DNS lookup takes some time, right? And we've
36:22
seen before, the number of concurrent connections to the same server is limited. So the idea, of course, is each and every resource could be on a different server, on a different origin, on a different domain, meaning the browser can send a hundred concurrent HTTP requests to a hundred domain names,
36:45
and the page is loading blazingly fast. This sounds a bit stupid, and probably it's also a stupid idea, because these domain names, they need to be looked up. Some browsers cache longer, some browsers don't cache as long,
37:00
especially Firefox. Firefox does a lot of lookups, and you know, if you're in shaky networks, I mentioned the hotel network before, sometimes DNS is a bit slow, and while other browsers still remember which domain is where, Firefox still does looking up, host, looking up, host. Well, you've seen, a few of you are nodding, so you've seen this before, right? So the idea is to say, okay, we just
37:22
use multiple domain names, but not so many. And there was this kind of rule of thumb, nine or so years ago, to shard on about four domains, right? Because you can do a lot of stuff concurrently, you have to trade off, you have the lookups, but I mean, still, you can load a lot in parallel. But well,
37:44
you saw, there are now exceptions to that rule. The first CSS, or the main CSS, should be loaded from the root domain. You can load JavaScript in a fashion, in a non-blocking fashion, so maybe that's not completely true today. And actually,
38:02
we always have to kind of find out, is the lookup time something that hurts us, or is the better throughput we get, because we have so many parallel connections, is that worth the effort? And I've spoken with a lot of companies of different sizes, and many of them who take performance seriously and do their
38:21
measurements, they say, okay, their kind of rule of thumb is, sharding on two domains is good. They get the best results if they shard on two domains, usually for some static content, one domain, except for, you know, the first CSS, and all the other content from the root domain. And of course you can say, well, you know, we have not only Moore's law, but bandwidth is getting
38:42
better and better, so this is a problem that kind of solves itself over time. But you saw, average website is over two megabytes. That was much, much less nine years ago. Also, if bandwidth gets better, the performance doesn't get better in the linear fashion. So the link here shows a very, very fascinating study,
39:06
and the result was that even if you have over twelve hundred percent more bandwidth, performance only goes up by a bit over fifty percent. So that's a limitless performance optimization, right? If we have HTTP2 or not, when we
39:22
have HTTP2, there is actually no need for sharding domains anymore, because we just have one TCP connection to that domain, and then have super performance data exchange. So maybe even the extra domain for static content isn't a problem any longer, except for if you have a lot of cookies and do need a cookie-less domain. Also there's something the W3C is working on, and I hope
39:45
browsers catch up with rather quickly. We can preload resources and provide the browser with hints what to do with those resources, and there are a few
40:00
options which we have. So one is to use link rel equals preload. What this does is it instructs the browser parser that this resource here will be needed later on. So the browser can start downloading it in the background, without you really, really noticing, and then that is kind of cached locally, right?
40:27
When you go to another page and that resource has a do not cache header, then of course no caching is in place, but until then it's preloaded and is ready for use, right? So when the browser has the time
40:40
and has the bandwidth and the resources, this could be preloaded. We have some other values for for rel. DNS prefetch, very interesting, that does the DNS lookup. So it says okay look up that URL, the DNS, I might need it later. So we have that information. When the browser has the time, the browser
41:00
does this. Or preconnect, right? So the browser then shall already open a TCP connection to the target and hold it, right? If you have cores in place, of course that's then still valid, but the TCP connection is opened and
41:22
held. So once you go to that page, the expensive TCP connection is already there. And even better, prerender. Prerender is fascinating, but it requires some effort from you. So what prerender does is it instructs the parser of the browser to load the page or the URL that you provide there and parse it and
41:45
render it and execute it. So it's like opening a page in an invisible tab and once you click on the link, show me that page, it's already rendered. It will be there immediately. Great effect. Well depending on the use of JavaScript that
42:03
is, because you probably need to distinguish between the page is rendered and the user sees it and the page is rendered, but the user doesn't see it because it's prerendered. So there is a visibility state property, we
42:21
have the onVisibilityState event handler and we can have a look at that handler or we can instruct the browser to well render the page and then fire the onLoad event handler. So that's the options we will have. Very, very interesting stuff. So really, really looking forward to that catching on
42:41
because it allows us, especially if we find out that most users just click on the first thing on our page, that we already prerendered once the browser has the time making our application look even faster than before. So I'm really excited about that and it just shows how all parties involved, browser developers, W3C and others try to get, try to put
43:04
performance on top of their agenda. Okay, number 10. Almost there. Minify JavaScript and CSS. Yeah, I mean, I don't have to say anything more about it, right? We have limited charset, so it's excellent. We get
43:22
excellent bandwidth gain by minifying that content because files get smaller, well, significantly. Usually we can save for about 60-70% by minifying it. So minifying, of course, means removing white space, renaming
43:41
variables, renaming functions, so there's just shorter and more compact and of course most of these minifiers create a map of, okay, that's what the variable is called now, that's what it was called earlier, so that we can have debugging support in all major IDEs. So that is still true. The only question, of course, is, and I get this often, what's the best minifier out there?
44:05
Your mileage may vary, but so I've been polling a lot of people and uglify.js, which is the one among other things jQuery is using, still seems to give the best results. There are also online kind of comparison sites,
44:21
so here's just one I picked. I don't even know if I want to upload JavaScript code to an external site. I mean, since it's JavaScript code, kind of everybody can see it when I put it on my website and from a security space there's also good progress in turning minified JavaScript code into legible JavaScript code, but you can compare a few there, but uglify.js is not part of
44:43
this, so for websites we do where performance is a key priority, what we do is we just run the tests ourselves, right, and see how small those files get and what gzipping does to them. So the result after minifying is not really interesting. The result after minifying and gzipping, that's the one that
45:01
counts, so we should absolutely use that. So that rule is still true. All right, the remaining performance rules are probably not that uber interesting, so most of them I will talk about real quick. Avoid redirects. I don't know if this
45:21
deserves number 11 in that list, but basically if you have a directory and you link to that directory and you omit the slash, then we get a second redirect. So we go to a slash name of directory and then get redirected to slash name of directory slash. It's just next HTTP request which with HTTP 2
45:45
hurts even less than this would hurt now, but I mean still it's easy to overlook and so just make sure that your URLs are correct. I know it's hard these days because even in books we get like incomplete URLs. I mean HTTP
46:01
colon slash slash ndcoslo.com is an incomplete URL because it's just a domain name, but we need the path, so we need a trailing slash. On the other hand, browsers do that in that specific case automatically, so that's no good reason behind that, but still make a link to complete URLs. It's just a small
46:24
thing, but really it's worth it. Number 12, remove duplicate scripts. Back then in 2007, so I took the information from there, they found out that two of the top 10 US sites, according to Alexa, load scripts twice. I
46:43
mean this can happen, right? So you have your base page and your base template and that base template is using, let's say jQuery, and then you have a widget on that page and that widget is using jQuery as well. If you are lucky, the widget uses the same jQuery version. If you are lucky, the
47:04
existing jQuery instance, no actually if you are lucky it doesn't check for an existing jQuery instance, because then the second jQuery overwrites the first. If it checks for an existing jQuery instance and then creates a copy, you have two jQuery instances running over that page and watching for DOM events.
47:22
So if possible, of course, avoid that. I mean in general, if you execute JavaScript code twice, usually we have a lot of closures, so we have very very little very little global variables and global functions, but even so, I mean if you have function X in JavaScript that's just shorthand for var X equals
47:42
function, so you just create a variable called X. So if you do that twice, well the second variable overwrites the first. So from the workings of the page nothing changes. Still should work as before, but if you just reference external files, the same file twice, depending on the browser, well maybe the
48:04
file is cached, right? So it's only loaded once, but it's still executed twice, which just makes it take longer. Rule number 13, and that's the last one we will talk about for a bit, configure ETags. So we have an HTTP header, ETag,
48:21
and the value of that header is a fingerprint for a resource, right? So there are different ways of calculating that, and the idea here is that this ETag header is sent along with the resource, so the browser sends let's say an image to the client, and says okay the the fingerprint of that is this and that,
48:42
and on subsequent requests the client says okay please give me that image, by the way I have this ETag information, and so the server then can decide okay the image I have here has the same ETag as the one the client just sent. I do not need to send the image because the client already knows. Awesome! Kind of
49:05
awesome. So I have here a screenshot from our company's page, and what you see here is what I call zombie 304 requests. 304 is the HTTP status code
49:22
for not modified. So basically when you go to web page test, right, so it loads the website, it gives you the chart, and then it loads the website again to see how well caching works, and that's the repeat view table for our website, and what happens in repeat view is okay the home page is cached,
49:43
which is okay, right? Then it loads information that's kind of dynamically generated, like here a portion of the CSS and later a little bit of monitoring like Google Analytics and others, but for all the JavaScript and static CSS files
50:01
the client remembered the file content due to caching, and the client remembered the ETag and sent it back to the server in their request. So the client does request all of those files, although they are in cache, because the ETag might have changed. Another option is the last modified header, so the server can say okay here's the image, it was last modified last week, and then
50:25
the browser kind of remembers that, and then the next request to the server says okay I already have that file, last modified this date, is there something new? Well there is nothing new, so these requests are pretty quick, right? They just take a little bit of time, but still these requests are made.
50:43
These requests are made and they are unnecessary. If you just did proper caching and try to say okay rule number two expires, if you set the expires header, okay this resource expires then and then, then these requests would not be made, but since we have ETags these requests are made
51:01
and that's a problem. We get a lot of these summary requests and they just take too much time, so we could make the performance of the website much, much better if you avoided them. And actually if we have a look at the NDC Oslo page, so that's the first loading of NDC Oslo, and oh sorry I
51:24
think it was the other tab, I have to find it, oh yeah that's a typical repute view of the site, so actually that's our company's homepage kind of new version. So you see on the repeat view, the first view, lot of requests and repeat view, almost no requests, right? So that's the
51:43
basic idea, that's the way it should look like. A repeat view should be lightning-fast, maybe load the HTML again, load dynamic information again like tracking, but that's it. No zombie 304 responses technically. And that's a common mistake and I see this pretty often and it's
52:02
actually a rather easy to get rid of, right? But you have to get rid of it. Alright, rule number 14 and then there you really see that it comes from 2007, make Ajax cacheable. So we talked a lot about caching and we do a lot
52:22
of HTTP requests. And of course all those caching rules still are true for services we call via XML HTTP request, but with the same caveats. So configure ETags, no we do not configure ETags, but we do gzip components, we do minify
52:42
JavaScript, we do avoid redirects, right? And finally, and that was not nothing of the original rules, but I still thought I should add this at the end, since we have or tend to have a lot of JavaScript code in our applications these days, of course the JavaScript should be optimized. First of all I have
53:03
to say, I mean optimizing JavaScript there are some, you know, some rules that are always true, but browsers today, they are really aggressively optimizing JavaScript code. There are kind of performance tests where the execution time for JavaScript is very close to kind of C code, which is
53:22
amazing, really really amazing. So some of the tips you find on the Internet, they were true for older browsers, but they are just not true anymore. So for instance, string concatenations are slow, you have to put everything in an array and then you have to concatenate the values in the array using the join method, arrays have in JavaScript. Yeah that was
53:44
true once, but it ain't true today for modern browsers, because they are very efficient at concatenating strings and they detect if, you know, we have a loop and concatenate strings in that loop in an inefficient fashion. So there's the jsperf.com website which holds a lot of
54:01
comparisons and JavaScript performance tests and that's usually the to-go place. On the other hand, micro-optimization is never a good idea, so string concatenations usually are not a problem when you work with JavaScript. It's just stuff like scoping, so if you have a local variable, do declare it using the var keyword, because that saves us one lookup in the global table and
54:24
if we have like a nested structure, then it saves us even more lookups. We can cache variables and functions and well, the string concatenations already said, that's that's weird advice, so I wouldn't focus on string concatenations. But one thing you read very often is that DOM costs a
54:43
lot. Minimize DOM operations. That's kinda but not entirely true. The problem with DOM is not reading. The problem is writing. Well, actually the problem isn't even writing, because what's really costly is the layout, right? So
55:04
if it needs to get rendered, layout it again. Now what happens if you have a modern browser and change three DOM properties? How many layouts changes is that? It's one layout change, right? So when you have a couple of write
55:21
operations, you do a batch write, so to speak. After the last write, the browser updates the layout. They are very clever. However, if you read, write, write, read, write, and the second read accesses a property that was changed by the write, a dirty property, then the browser needs to layout. So read, write,
55:43
read, write usually means two layout operations. If you do first the two reads and then the two writes, or first the two writes and then the two reads, you only have one layout operation. So that's probably the best thing you can do there. Again, as browsers change, these requirements and
56:01
these advice might change as well. And that's true for everything we did today. So you saw that those rules that were created almost ten years ago, they are still mostly true, right? But some of them just changed, because browsers got better, bandwidth got better, servers got better, everything
56:24
gets better, but still performance is a lot of work. So this is a topic we, you, everybody should revisit many, many times again. All right, that's all I got. So thank you for coming. Safe travels home, no strike today, and enjoy the rest of your conference. Thank you till next time. Bye-bye.