[YUI] Maximizing Parallel Downloads in the Carpool Lane

making web/javascript 2008.11.10 23:58

HTTP/1.1 명세에 따르면 브라우저가 호스트당 두 개의 컴포넌트를 병렬적으로 다운로드 받는 것을 권장하고 있다. 대부분의 웹 페이지에선 하나의 호스트에서 받아오는게 보통.  그림 1처럼 계단식 패턴을 보이게 된다.

만약, 웹페이지가 두 개의 호스트에서 컴포넌트를 받아온다면, 응답 속도는 두 배로 빨라질 것이다.

병렬적인 다운로드를 호스트당 두 개씩이라는 건 그저 가이드라인이다. 기본적으로 IE, FF 모두 이 가이드라인을 따르고 있지만 사용자는 이 설정을 조절할 수 있다[각주:1].

HTTP/1.0에선 FF은 기본으로 호스트당 8개를 병렬적으로 다운로드하는게 기본값이다. 보나마나 그림 2보다 빠를테다.

사용자를 배제하고 서버에서 별 공수들이지 않고도 컴포넌트를 다수의 호스트로 분산시켜 다운받게 할 수 있다. 사용자의 bandwidth, CPU speed, 과도한 병렬 다운로드가 성능을 다운시킬 수 있다.

빈 HTML에 20개의 이미지를 넣고, 호스트를 1, 2, 3, 4, 5, 10으로 늘려 응답시간을 체크해보았다. 36*36의 작은 사이즈 20개 , 116*61의 중간 사이즈의 이미지 20개를 비교해보면... 중간 사이즈 이미지의 경우는 호스트가 늘어날 수록 응답 시간이 점점 늘어났고, 작은 사이즈 이미지의 경우엔 호스트의 증가에 별 영향을 받지 않았다. 평균적으로 호스트 수가 2개일 때가 가장 응답 시간이 좋았다.

이러한 결과에 영향을 준 범인으로는 사용자 환경에서 CPU를 생각해 볼 수 있다. 필자의 laptop에서 2개에서 20개로 병렬 다운로드가 증가할 때 CPU 사용율이 25%에서 40%까지 뛰었다.(사용자 환경마다 다르긴 하겠으나...)

이 결과는 도메인들이 이미 브라우저에 캐시된 상태에서다. (하루 방문자 중에 40~60%가 빈 캐시인 채로 접근을 한다고 함...)

(DNS lookup의 경우 똑똑한 서버 개발자에게 문의한 결과 별 영향이 없는 기반 구조라 했으니 패스.)

결론적으로 두 개에서 4개 정도의 호스트 숫자가 적당하다. 여튼, 페이지 내에 컴포넌트 숫자를 줄이는게 응답 시간을 개선하는 최고 방법이라는 말씀.

  1. IE <= 7, FF2에서 2개, IE8, FF3에서는 6개가 default. (http://yuiblog.com/blog/2008/07/22/non-blocking-scripts/) [본문으로]

Best practices for speeding up your web site

making web/javascript 2008.11.09 01:36
/ http://developer.yahoo.com/performance/rules.html
/ Yahoo! The Exceptional Performance team

Minimize HTTP Requests

80% of the end-user response time is spent on the front-end.
  • Combined files are a way to reduce the number of HTTP requests by combining all scripts into a single script, and similarly combining all CSS into a single stylesheet. Combining files is more challenging when the scripts and stylesheets vary from page to page, but making this part of your release process improves response times.
  • CSS Sprites
  • Image maps
    only work if the images are contiguous in the page such as a navigation bar. But, it is not accessible too, so it's not recommended.
  • Inline images not yet supported across all major browsers.
As described in Tennu Theurer's blog post Browser Cache Usage - Exposed!, 40~60% of daily visitors to your site com in with an empty cache.

Use a Content Delivery Network

Add an Expires or a Cache-Control Header
for static components: implement "Never expire" policy by setting far future Expires header
for dynamic components: use an appropriate Cache-Control header to help the browser with conditional requests.
At Yahoo! we often make this steo part of the build process: a version number is embedded in the component's filename, for example, yahoo_2.0.6.js

Gzip Components
Gzipping generally reduces the posponse size by about 70%.

Put Stylesheets at the Top
Putting stylesheets in the HEAD allows the page to render progressively.(Some browsers(including IE) block rendering to avoid having to redraw elements of the page if their styles change. The user is stuck viewing a blank white page.)

Put Scripts at the Bottom
The problem caused by scripts is that they block parallel downloads. The HTTP/1.1 specification suggests that browsers download no more than two components in parallel per hostname. While a script is downloading(not images), however, ths browser won't start any other downloads, even on different hostnames.
In some situations it's not ezsy to move scripts to the bottom. An alternative suggestion that often comes up is to use deferred scripts. The DEFER attribute indicates that the script does not contain document.write, and is a clue to browsers that they can continue rendering. (Unfortunately, Firefox doesn't support the DEFER attribute.)

Avoid CSS Expressions
CSS expressions are a powerful (and dangerous) way to set CSS properties dynamically. The problem width expressions is that they are avaluated more frequently than most people expect. Not only are they evaluated when the page is rendered anc resized, but also when the page is scrolled and even when the user moves the mouse over the page.
if the style property must be set dynamically throughout the life of the page, using event handlers instead of CSS expressions is an alternative approach.

Make Javascript and CSS External
question: Should Javascript and CSS be contained in external files, or inlined in the page itself?
The key factor is the frequency with which external JavaScript and CSS components are cached relative to the number of HTML. If users on your site have multiple page views per session and many of your pages re-use the same scripts and stylesheets, there is a greter potential bebefit from cached external files.
The only exception where inlining is preferable is with home pages, such as Yahoo!'s front page and My Yahoo!. Home pages that have few (perhaps only one) page view per session may find that inlining JavaScript and CSS results in faster end-user response times.
One such technique is to inline JavaScript and CSS in the front page, but dynamically download the external files after the page has finished loading. Subsequent pages would reference the external files that should alredy be in the browser's cache.

Reduce DNS Lookups
The DNS maps hostnames to IP addresses. It typically takes 20~120 milliseconds for DNS to lookup the IP address for a given hostname. The browser can't download anything from this hostname until the DND lookup is completed.
DNS looups are cached for better performance. Most browsers have their own caches, separate from the OS's cache. As long as the browser keeps a DNS record in its own cache, it doesn't bother the OS with a request for the record.
IE cache DNS loopups for 30 minutes by default, FF caches DNS loopups for 1 minutes.
Reducing the number of unique hostnames has the potential to reduce the amount of parallel downloading that takes place in the page. My guideline is to split these components acress at least two but no more than four hostnames. This results in a good compromise between reducing DNS lookups and allowing a high degree of parallel downloads.

Minify Javascript and CSS
Two popular tools for minifying Javascript code are JSMin and YUI compressor(also minify CSS).
Even if you gzip your scripts and styles, minifying then will still reduce the size by 5% or more.

Avoid Redirects
Use of redirects include using Alias and mod_rewrite if the two code path are hosted on the same server. If a domain name change is the cause of using redirects, an alternative is to create a CNAME(a DNS record that creates an alias pointing from one domain name to another) in combination with
Alias and mod_rewrite.

Remove Duplicate Scripts
Unnecessary HTTP requests happen in IE, but not in Firefox. In IE, if an external script is included twice and is not cacheable, it generates two HTTP requests during page loading. Even if the script is cacheable, extra HTTP requests occur when the user reloads the page.

Configure ETags
Entity tags(ETags) are an mechanism that web servers and browsers use to determine whether the component in the browser's cache matchs the one on the origin setver. (An "entity" is another word a "component": images, scripts, stylesheets, etc.) ETags were added to provide a mechanism for validating entities that is more flexible than the last-modified date. An ETag is a string that uniquely identifies a specific version of a component.
... problems ...
If you're not taking advantage of the flexible validation model that ETags provide, it's better to just remove the ETag altogether. Removing the ETag reduces the size of the HTTP headers in both the response and subsequent requests. In Apache, this is done by simply adding the following line to your Apache Configuration file: FileETag none

Make Ajax Cacheable
The most important way to improve the performance of Ajax is to make the responses cacheable, as discussed in Add a Expires or a Cache-Control Header. Some of the other rules also apply to Ajax: Gzip Components, Reduce DNS Lookups, Minify Javascript, Avoid Redirects, Configure ETags.
Even though your Ajax responses are created dynamically, and might only be applicable to a single user, they can still be cached. Doing so will make your Web 2.0 apps faster.

Flush the Buffer Early
In PHP you have the function flush(). It allows you to send your partially ready HTML response to the browser so that the browser can start fetching components while your backend is busy with the rest of the HTML page. The benefit is mainly seen on busy backends or light frontends.
A good place to consider flushing is right after the HEAD because the HTML for the head is usually easier to produce and it allows you to include any CSS and JavaScript files for the browser to start fetching in parallel while the backend is still processing.
Yahoo! search pioneered research and real user testing to prove the bebefits of using this technique.

Use GET for AJAX Requests
The Yahoo! Mail tean found that when using XMLHttpRequest, POST is implemented in the browsers as a two-step process: sending the headers first, then sending data. So it's best to use GET, which only takes on TCP packet to send (unless you have a lop of cookies). The maximum URL length in IE is 2K, so if you send more than 2L data you might not be able to use GET.

Post-load Components
"What's absolutely required in order to render to page initially?"
JavaScript is an ideal candidate for splitting before and after the onload event.
Tools to help you out in your effort: YUI Image Loader, YUI Get utility

Preload Components
There are actually several types of preloading:
Unconditional preload: as soon as onload fires, you go ahead and fetch some extra components. Check Goole.com for an esample of how a sprite image is requested onload.
Conditional preload: based on a user action you make an educated guess where the user is headed next and preload accordingly. On search.yahoo.com you can see how some extra components are requested after you start typing in the input box.
Anticipated preload: preload in advance before launching a redesign. Your old site can use the time the browser is idle and request images and scripts that will be used by the new site.

Reduce the Number of DOM Elements
YUI CSS utilities
How many DOM elements are too many? For example the Yahoo! Home Page is a pretty busy page and still under 700 elements (HTML tags).

Split Components Across Domains
Make sure you're using not more than 2~4 domains because of the DNS lookup penalty.
For more informatin check "Maximizing Parallel Downloads in the Carpool Lane" by Tenni Theurer and Patty Chi.

Minimize the Number of iframes
<iframe> pros:
- Helps with slow third-party content like badges and ads
- Security sandbox
- Download scripts parallel
<iframe> cons:
- Costly even if blank
- Blocks page onload
- Non-semantic

No 404s

Reduce Cookie Size
"When the Cookie Crumbles"
by Tenni Theurer and Patty Chi

Use Cookie-free Domain for Components
When the browser makes a request for a static image and sends cookies together with the request, the server doesn't have any use for those cookies. So they only create network traffic for no good reason. You should make sure static components are requested width cookie-free requests. Create a subdomain and host all your static components there.

Minimize DOM Access
- Cache references to accessed elements
- Update nodes "offline" and then add them to the tree
- Avoid fixing layout with Javascript
For more information check "High Performance Ajax Applications" by Julien Lecomte.

Develop Smart Event Handlers
Using event delegation is a good approach.
DOMContentLoaded is the event you might consider using instead of onload, but until it's available in all browsers, you can use the YUI Event utility, which has an onAvailable method.

Choose <link> over @import
In IE @import behaves the same as using <link> at the bottom of the page, so it's best no to use it.

Avoid Filters
The problem with AlphaImageLoader filter is that is blocks rendering and freezes the browser while the image is being downloaded. It also increases memory consumption and is applied per element, not per image, so the problem is nultiplied.
The best approach is use gracefully degrading PNG8 instead, which are fine in IE. If you absolutely need
AlphaImageLoader, use the underscore hack _filter as to not penalize your IE7+ users.

Optimize Images

Optimize CSS Sprites
Arrainging the images in the sprite horizontally as opposed to vertically usually results in a smaller file size. Combining similar color in a sprite helps you keep the color count low, ideally under 256 colors so to fit in a PNG8.
"Be mobile-friendly" and don't leave big gaps between the images in a sprite.

Don't Scale Images in HTML
Don't use a bigger image than you need just because you can set the width and height in HTML.

Make favicon.ico Small and Cacheable

Keep Components under 25K
iPhone won't cache components bigger than 25K. (uncompressed size)
"Performance Research, Part 5: iPhone Cacheability - Making it Stick" by Wayne Shea and Tenni Theurer.

Pack Components into a Multipart Document
Packing components into a multipart document is like an email with attachments, it helps you fetch several components with one HTTP request (remember: HTTP request are expensive).

인생 뭐 있냐...

making web/javascript 2008.04.09 15:44
사용자 삽입 이미지


/* 몇 시간 쯤 공부 쫌 한 후... am 2:25 */

우후후훗... - -;

게슴츠레 시작하여
프로젝트 말미에는 불가항력적으로다 ruby를 했던 때가 생각이 난다.

아니 아마 꽤나 좀 피곤은 해지겠다마는...
뭐... 인생 별 거 있겠냐.

뭐가 왜 어때서. - -b


Type: Number

2008.03.11 02:21

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력해주세요.


making web/javascript 2008.02.24 23:46
The ceil() method returns the value of a number rounded UPWARDS to the nearest integer.


Math.ceil(0.60) -> 1
Math.ceil(-3.14) -> -3
Math.ceil(5.1) -> 6

티스토리 툴바