Performance 1

In terms of speed, reliability, and responsiveness

Intro

In client/server relationships, performance level quality is determined by:

  1. Server software, hardware, application architecture

  2. Amount of data transmitted up/downstream

  3. What transmitted data asks the recipient to do

  4. Client hardware, network connection speed

  5. Demand volume

Adequate performance levels are for serving valued clients with the slowest devices and connections. Everyone else gets a faster experience, and hopefully very few clients won't get a usable one at all.

User Experience

Regarding UX, how do the following areas of performance...

Area

Factors

Speed

Network communication, application architecture, code efficiency, data volume, caching

Reliability

Service availability; consistently meeting expected behavior

Responsiveness

Code efficiency, providing timely feedback during interaction

...for each 1sec of delay affect:

  • User abandonment

  • Organization goals

  • Word of Mouth

Symptoms:

  • Speed (actual v. perceived)

  • Flash of unstyled content (FOUT)

  • Content shifting

  • Scrolling is janky or frozen

TODO For info on responsiveness and feedback, see this section on UX (LINK)

Application Architecture

While a variety of architectures can serve the same purpose, increased network demand requires doing one or more of the following at a high level:

  • Upgrade hardware

  • Replicate the application and its services

  • Decouple application components and create workflows

  • Use concurrent systems with actors or channels

As systems grow, the key to any architectural performance improvement is to know whether it's needed, and if so, where. This is handled through cloud service dashboards, designated observability points, and DevOps personnel.

The keys to optimizing production and delivery are:

  • Understanding data demand patterns and usage needs

  • Create and deliver static data by default (made-to-stock)

  • Store data in caches and CDNs and restock at given or calculated intervals

  • Create and deliver dynamic data when necessary (made-to-order)

TODO: client architecture

Servers managing routes, application logic, delivering web pages

Clients managing routes, application logic, resources plus possible need for server-side rendering.

What are the performance implications? Vaguely:

  • Traditional is faster for first loads, slower on subsequent

  • SPAs are slower for first loads, faster on subsequent

In the end, perhaps can take it for granted that the server (organization) uses a decent stack with a CDN for cached files and a host that reasonably handles demand. That leaves just optimizing the data transmitted within a server's control.

Frontend Considerations

Devices and Network Speeds

PC -> slow or fast connection Mobile -> slow or fast connection Test on representative, average mobile devices because JS parsing times can be up to 3x as slow as the newest, premium phones.

Time to Interactive

TTI is the total time in seconds from first request to being ready for user interaction. It entails first resolving the address, making the initial request, and receiving the HTML.

After loading HTML, it is synchronously parsed from top to bottom, pausing to download and parse any CSS and JS. Images are added to a queue for later downloading.

Parsing CSS is fast but large JS bundles of 1MB can take an average of 700ms (2017). Painting begins and finally a page will be interactive.

The Stages are as follows:

  • Initial Request

  • First Paint - anything other than a white screen

  • First Contentful Paint - logos and graphics start to appear

  • First Meaningful Paint - visible text

  • Visually Complete - when viewport content finishes loading and rendering

  • First Interactive - Scrolling possible and buttons/link work

With variable network connections and device CPUs, placing the software architecture on the client can have significant performance penalties. While client-side asset caching and subsequent server-side rendering do help, the initial loading and parsing times are still quite important. After all, product performance is all that matters to users.

So regardless of whether clients initially load/parse individual webpages or view applications, the time-to-interactive stage should be minimized to an acceptable level, based on product expectations.

TODO starting network requests using "navigationPreload" while Service Workers are booting up

Network Communication

Intro

Being aware of how HTTP communication works, web developers can influence performance architecting their applications to spend less time making network connections, and sending more data with each each request.

For more info, see TODO LINK

Process: DNS Lookup Server Connection Server Request Server Response

DNS Lookups

TODO Lookup time for each domain. Can premium DNS providers help? At least with security and availability Supposedly large hosts DNS services are slow, whereas those from Google, AWS, etc. are fast. Consider how cellular connection speeds can be slower and inconsistent affect each request time. Navigation Timing API can be helpful?

Preloading in HTML Head

DNS-Prefetch link rel="dns-prefetch" presolving DNS for primary resource origins

Preconnect link rel="preconnect" "Gives a hint to the browser to begin the connection handshake (DNS, TCP, TLS) in the background to improve performance" This is particularly good for embedded content that is loaded/streamed from 3rd parties.

Prerender link rel="prerender" Unfortunately downloads an entire page, executes arbitrary JS in the background, and uses a lot of CPU. Google AMP works around this by prerendering only the first viewport, without executing any 3rd party JS.

Preload link rel="preload" as="content-type" (script, other. See HTMLLinkElement.as) "browsers can be informed to prefetch resources without having to execute them, allowing fine-grained control over when and how resources are loaded." The "as" property allows browser to customize load optimization based on resource type (video, audio). Cuts round-trips, supports cross-origin requests, has load/error events, content negotiation.

For preloading a chunk of a large resource, use as="fetch". Then this can be used with Media Source Extensions, a well-supported JavaScript API that lets you build streams for playback from segments of audio or video. Netflix, Twitch, and YouTube use this extensively to quickly play video.

Allows you to force the browser to request a resource, while the html is still downloading, without blocking the window.load event. Such resources are stored in the browser and are inactive until explicitly referenced in the DOM, JavaScript, or CSS. If the preload request hasn't completed by the time the resource is needed, a normal network request will be sent.

Preloading in HTML Body

Element attribute preload="auto". Note: Such resource fetching will only begin when the initial html document has been completely loaded and parsed. The window load event will fire when the resource has been fetched.

Note: This is just a hint to the browser and is subject to compatibility, and whether users have enabled "save-data" in their browser settings to limit bandwidth consumption.

Links can be preloaded right before they're clicked using libraries like Instant.Page and InstantClick

Preloading with Service Workers

starting network requests using "navigationPreload" while Service Workers are booting up

Respectful Preloading

Preloading requires battery life, memory, network connection type, possibly a video poster image, and device storage from users. Preloading may not be in their interest. We can use progressive enhancement to test for specified conditions in the user browser, and decide to adjust preloading (ex: return lower quality media) or dismiss preloading altogether. Device browser attributes to test include: navigator.{getBattery, deviceMemory, storage, connection}.

Two other ways to test whether perhaps resources should load. The Page Visibility API can tell if a document is currently visible on the display or not. The IntersectionObserver allows us to track when an element enters or leaves the browser viewport. If there's many large media assets on the page, resetting the video src attribute to null on viewport exits can significantly decrease the page's bandwidth consumption. Particularly important for webpages using infinite scroll.

Lastly, one can check if users have enabled "Save Data" in their browser, and then decide to preload accordingly.

Resource Locations

Your server, bundled or not CDNs Cache

Load Considerations

Think about when/how to load assets Only load assets when they add value Lazy load non-critical resources

Progressive Rendering

Stream HTML responses

Use HTTP/2 Server Push

Single connection and handshake with server pushing additional resources, eliminating subsequent round trip communication costs for each resource.

Although great, it's not cache aware and has no prioritization options. Two options include:

  • Cache digests

  • Service Workers

Volume

No dev code to production

Code Tips

Flexbox or Grid instead of CSS layout frameworks and/or JS. Semantic HTML to possibly reduce the total number of code characters CSS selector-based DOM traversal without using JS DOM libraries.

Removing Unused Code

CSS/JS, Tree Shaking (bundler)

Selective loading, serve modern browsers ES2015

Network Connection API

Not widely supported outside Chrome, but it allows to check by type (wifi, 2G, etc), which allows us to load resources accordingly.

User Preferences

Perhaps users prefer faster performance or simpler experiences, and would rather opt-in to rich media through user settings (saved in SW) and/or by default.

Users on slow networks often disable network access by default and enable it when ready to use the web. Or perhaps data plans are expensive, and it's courteous to respect.

Save-Data Request Header

Some browsers support providing a "Save-Data: on" attribute when making requests to web servers. Developers can take advantage of this by checking the headers or via JS.

Caching

Network

Anything to get resources closer to users: CDNs

Browser

TODO: Use include caching info from MDN, Google

Set Cache Headers

First load what's needed to get to interactive. Later load anything else foreseeably needed. Unused JS upfront just delays TTI. Code splitting allows creating smaller bundles whose loading we can prioritize.

Service Workers caching with IndexedDB

Resources

Intro

Only require resources which provide significant value. Otherwise it just increases loading/parsing time, wastes developer time, and perhaps negatively affects performance and UX. Assuming one has already tackled whether to use libraries, frameworks, or to build a single page application (SPA), all sites should first load:

  • Critical-path HTML, CSS, JS

Followed by optional:

  • Service Workers

  • Router, State management, Utilities

  • Framework library

  • Application logic

  • Fonts and Media

Can any resources can be inlined to the HTML for fewer network requests?

HTML

TODO Minify, pre-compress

Fonts

"font-display: optional" controls what fonts will render depending on their load time. If they take too much time over the network, they won't be used. If they're already available in the cache, use them.

Font caching?

Subset custom fonts to download only what will be used, though allowing common letters like umlauts and punctuation common in other languages in case data received or user-entered uses them.

Other considerations?

CSS

Inlining CSS means fewer external resource requests, although perhaps inappropriate for larger websites and style systems.

Using modular or composable CSS, you can reuse styles more often, and update only parts when needed, thus leaving the okay parts in the cache.

Compile CSS with decimal precision (ex: 4 places) Fallbacks for previous browser versions (ex: 2) with autoprefixer tools Shorthand declarations if possible Remove any unused declarations or rule sets Brotli and Gzip individual files, with normal file for unsupported file

JavaScript

First load what's needed to get to interactive. Later load anything else foreseeably needed. If non-SPA, load main JS at bottom of HTML document, followed by page-specific JS. To avoid race conditions and ensure dependencies available, keep main JS file as normal (synchronous) without async or defer.

Using "defer" as script tag attribute, "allows the external JavaScript file to run when the DOM is loaded, without delaying page load first."

Using "async" as script tag attribute, "allows the external JavaScript file to run when it's available, without delaying page load first."

requestIdleCallback

From MDN:

"Method queues a function to be called during a browser's idle periods. This enables developers to perform background and low priority work on the main event loop, without impacting latency-critical events such as animation and input response. Functions are generally called in first-in-first-out order; however, callbacks which have a timeout specified may be called out-of-order if necessary in order to run them before the timeout elapses.

You can call requestIdleCallback() within an idle callback function to schedule another callback to take place no sooner than the next pass through the event loop.A timeout option is strongly recommended for required work, as otherwise it's possible multiple seconds will elapse before the callback is fired."

Unused JS upfront just delays TTI.

Code splitting into many bundles (ex: by route)

Write modular JS, grouped as appropriate (functionality, route) Not all visitors enter through same route, nor visit all routes. Some visitors browsers' may not support certain features, for which any associated JS shouldn't be loaded

Options: - Combine files based on folder structure - Use module closures to isolate from other JS in global namespace - Perhaps keep polyfills in separate files.

Tree-shaking to remove unused code. By examining during production builds what's actually used from an imported module, a compiler can reformat both important statements and module scripts to only include what get's used.

Minify / uglify

Bundling If Manual, concatenating may need to be ordered. Some tools like in gulp will use alphabetical order in folders. Renaming initial files with underscores, and last files with symbols like Omega can help code position in concatenated files.

If Automated, Code splitting allows creating smaller bundles whose loading we can prioritize.

Statistics (2017 Chrome Dev Summit), show that JS is requested from memory and disk only ~50% of the time. Fonts also perform poorly. This could be for various reasons including:

  • New releases are pushed too often, thereby invalidating cached data

  • Insufficient HTTP caching headers set

3rd Party JS

For ad content, consider sandbox iframes because they :

  • use synchronous scripts

  • document.write more sync scripts

  • do style recalculations which is heavy on CPU because it entails scanning your entire page, and named styles may override your styles

iframes limit their interaction to just their domain.

Media and Formats

Are They Needed?

Important question as they can take a lot of space in the total payload. Is it filler or stock that adds no extra value? While perhaps emotionally appealing, if the benefit is small, it'd be better served ONLY for fast connections. (TODO: Is that actionable?)

Ex: If an article is about net security, is a stock photo of a hacker really needed?

Another aspect to consider is how images add value to content blocks as headers. When screens are large, images can provide greater emphasis to important content blocks, or help differentiate them. However when screen sizes are small and blocks are vertically stacked, images may lose this supplementary value and their use should be perhaps limited to where they provide the greatest value.

TODO: discussed in Aaron Gustafson presentation and likely elsewhere There is an opportunity to use JS to insert images via data-img attributes, which could be appended to elements ??? depending on screen sniffing, media queries. Maybe the picture element is injected dynamically instead to allow multiple formats.

From Aaron Gustafson "Performance as User Experience":

  • Does the image reiterate info found in the surrounding text?

  • Is the image necessary to understand the surrounding content?

  • Does the image contain text?

  • Is the image a graph, chart, or table?

  • Could the content of the image be presented in a different format that wouldn't require an image?

  • Is the image purely presentational?

Formats and Compression

Refer to Images.guide for comprehensive info. But the gist is:

Automate media compression through build process, CDNs, or other providers. Build process tools include managers (Mac: ImageOptim) and format encoders.

Remove metadata to save size. Use SVG for non-photo graphics and animation. In code, include fully supported formats first, followed by modern formats.

Type

Formats

Encoding

Age

Video

WEBM

VP9

Modern, good support

Video

MP4

H.264

Full support

Photo

WEBP

VP8

Modern, good support

Photo

JPG

MozJPEG

Full support

Illustration

SVG

various

BEST, Full support

Illustration

PNG, GIF

various

Full support

Illustration

Base64

various

Full support

Animation

WEBM

VP9

Modern, good support

Animation

SVG

various

Full support

Additional Tactics

Consider preloading and lazy loading to prioritize assets. Fore preloading, see previous section under Network Communication.

Consider selective serving media through HTTP accept-headers Use tools to generate media srcset breakpoints Use client-hints for automated browser asset selection (good support)

Troubleshooting

FOUT

Consider applying 0 body opacity until JS runs and resets it. If there's no JS, include a noscript tag to unhide the body. If JS servers take too long, you can apply a timeout rule and CSS keyframe animate the body from 0 to 1.

Content Shifting

Use placeholders for known ad spots with static resource sizing.

Janky / Frozen Scrolling

Calculate restyles only when things resize Batch DOM accesses and changes low styles recalcs per frame, and per page load.

Slow Animations

Transforms and Opacity is best handled by CSS for hardware accelerated performance (GPU), sequentially timed animations by JS.

Last updated