5. You’re driving a brand new car
It’s moving really slowly
You can smell smoke
People are giving you looks
Friday, March 6, 2009
6. You’re driving a brand new car
It’s moving really slowly Return the car
You can smell smoke Rebuild the car
People are giving you looks Disengage the hand brake
Friday, March 6, 2009
9. THE POINT
LOW-HANGING FRUIT…
Friday, March 6, 2009
10. YAHOO!
THEY MAKE WEBSITES
Friday, March 6, 2009
11. Yahoo!
34 “Best Practices” for Exceptional Performance
1. Minimize HTTP 9. Reduce DNS Lookups 19. Reduce the Number of 27. Choose <link> over
Requests DOM Elements @import
10. Minify JavaScript and
2. Use a Content CSS 20. Split Components 28. Avoid Filters
Delivery Network Across Domains
11. Avoid Redirects 29. Optimize Images
3. Add an Expires or a 21. Minimize the Number
12. Remove Duplicate 30. Optimize CSS Sprites
Cache-Control of iframes
Scripts
Header 31. Don’t Scale Images in
22. No 404s
13. Configure ETags HTML
4. Gzip Components
23. Reduce Cookie Size
14. Make Ajax Cacheable 32. Make favicon.ico
5. Put Stylesheets at the
24. Use Cookie-free Small and Cacheable
Top 15. Flush the Buffer Early Domains for
33. Keep Components
6. Put Scripts at the Components
16. Use GET for AJAX under 25K (mobile)
Bottom Requests 25. Minimize DOM
34. Pack Components
7. Avoid CSS Access
17. Post-load into a Multipart
Expressions Components 26. Develop Smart Event Document (mobile)
8. Make Javascript and Handlers
18. Preload Components
CSS External
Friday, March 6, 2009
12. YSLOW
PLUGIN FOR FIREBUG
Friday, March 6, 2009
13. YSlow
Tests Your Site on 13 Rules
Minimize HTTP Put Stylesheets at Reduce DNS
Requests the Top Lookups
Use a Content Put Scripts at the Minify JavaScript
Delivery Network Bottom and CSS
Add an Expires Avoid CSS Avoid Redirects
or a Cache- Expressions
Remove
Control Header
Make Javascript Duplicate Scripts
Gzip and CSS External
Configure ETags
Components
Friday, March 6, 2009
15. Minimize HTTP Requests
HTTP Requests are Expensive!
Net work latency + server latency + net work latency + download time.
This rule is fundamental, the Catch-All. Everything else is based on it.
Asset packaging reduces overall number of requests.
Compression & minification reduce the size of responses.
Reordering includes lets important stuff go first.
ETags and Last-Modified checks reduce the content of an HTTP Request.
Browser caching completely eliminates subsequent requests. Major win.
Friday, March 6, 2009
17. Make Javascript
and CSS External
Easier to maintain, less HTML to send
Browser caching prevents later requests
Help order requests by priority
Rails makes this pretty easy,
stylesheet_link_tag
javascript_include_tag
Friday, March 6, 2009
21. Stylesheets at the top
Scripts at the bottom
<!DOCTYPE html>
Javascript shouldn’t block <html>
<head>
more important rendering
<title>Your Awesome Site</title>
<%= stylesheet_link_tag 'application' %>
<%= yield(:head) %>
</head>
<body>
Pass blocks to the head and <div id=quot;pagequot;>
<header>
foot from within your views: <h1>Awesome Site</h1>
</header>
<article>
<%= yield %>
<% content_for(:foot) do %> </article>
<%= javascript_tag quot;…quot; %> </div>
<%= javascript_include_tag 'application' %>
<% end %> <%= yield(:foot) %>
</body>
</html>
Friday, March 6, 2009
29. Apache gives this for free for most of your
assets.
Rails gives this for free for all of the
responses it sends back. It also has some
helpers that will let you potentially skip db
calls and expensive view rendering.
CONFIGURE ETAGS
Friday, March 6, 2009
30. Configure ETags
Web servers likely give you a lot for free
Rails gives you some ETagging for free
Rails ETag and Last-Modified helper
def show
@article = Article.find(params[:id])
if stale?(:etag => @article, :last_modified => @article.created_at.utc)
@statistics = @article.really_expensive_call
respond_to do |format|
# etc
end
end
end
Friday, March 6, 2009
31. ADD AN EXPIRES OR
CACHE-CONTROL HEADER
Friday, March 6, 2009
32. Add an Expires Header
Check out mod_expires for Apache
Rails already does “soft” cache busting
Hard cache busting (for CDNs)…
config.action_controller.asset_host =
quot;http://assets.example.com/#{REVISION}quot;
Friday, March 6, 2009
34. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new(quot;Uploading...quot;, files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,quot;#{REVISION}/quot;)
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
Friday, March 6, 2009
35. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new(quot;Uploading...quot;, files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,quot;#{REVISION}/quot;)
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
Friday, March 6, 2009
36. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new(quot;Uploading...quot;, files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,quot;#{REVISION}/quot;)
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
Friday, March 6, 2009
37. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new(quot;Uploading...quot;, files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,quot;#{REVISION}/quot;)
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
Friday, March 6, 2009
38. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new(quot;Uploading...quot;, files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,quot;#{REVISION}/quot;)
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
Friday, March 6, 2009
39. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new(quot;Uploading...quot;, files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,quot;#{REVISION}/quot;)
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
Friday, March 6, 2009
40. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new(quot;Uploading...quot;, files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,quot;#{REVISION}/quot;)
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
Friday, March 6, 2009
41. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new(quot;Uploading...quot;, files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,quot;#{REVISION}/quot;)
# Upload the object to S3
# TODO: set the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
Friday, March 6, 2009
42. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new(quot;Uploading...quot;, files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,quot;#{REVISION}/quot;)
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
Friday, March 6, 2009