Frontend Performance Optimiziation
Step by Step with Symfony2
phpday.it, 18.5.2012 © David Buchmann, Liip AG
What makes a website slow?
- Inefficient application code
- Response time
- Bandwith
- Browser side rendering
What makes a website slow?
- Inefficient application code
- Response time
- Bandwith
- Browser side rendering
Measure - change - measure
Jordi Boggiano
Find out the effects of your changes.
Remember:
«Premature optimization is the root of all evil»
Donald E. Knuth
I am going to talk about:
- Frontend performance analysis and basic tips
- Assetic to get serious about css and js optimization
- Caching
- Varnish and ESI
I am not going to talk about:
- PHP code optimization
- PHP accelerators like APC
- Webserver optimization
Overview:
- Frontend performance analysis and basic tips
- Assetic to get serious about css and js optimization
- Caching
- Varnish and ESI
Tools and helpers
- firebug / chrome debug toolbar
- YSlow: yslow.org
- netem (linux): Simulate slow network
$ tc qdisc add dev lo root netem delay 300ms 30ms loss 0.10%
Caution: This will affect everything on your machine
- Sloppy: www.dallaway.com/sloppy/
- Decrease Apache performance: SetEnv no-gzip, KeepAlive Off
- Decrease Symfony2 performance: twig > cache: false
Demo application
- Controller with usleep
- Artificially bloated number and size of javascript and css
- But big projects can have those numbers
- In-memory login and a form to show interaction
Measure
- 2 seconds to load HTML page
- Tons of CSS files
- Tons of JS files
- Page is rendered after about 10 seconds
- Once rendering is done, requests the images that are visible
Measure
- 2 seconds to load HTML page
- Tons of CSS files
- Tons of JS files
- Page is rendered after about 10 seconds
- Once rendering is done, requests the images that are visible
About 10 seconds until the user sees a page
Optimization: Move javascript to the bottom

@@ -6,8 +6,6 @@
{% include 'DbuCoreBundle::stylesheets.html.twig' %}
- {% include 'DbuCoreBundle::javascripts.html.twig' %}
<link rel="shortcut icon" href="{{ asset('favicon.ico') }}" >
</head>
@@ -91,5 +89,11 @@
</div>
+ {# javascript at the bottom: browser waits with rendering
+ until all files referenced before are loaded
+ #}
+ {% include 'DbuCoreBundle::javascripts.html.twig' %}
</body>
Measure:
We just doubled the speed the user perceives!
Browser starts rendering after as soon as it has all CSS files, no need to wait for javascript to load.
Page renders after about 5 seconds
Measure:
We just doubled the speed the user perceives!
Browser starts rendering after as soon as it has all CSS files, no need to wait for javascript to load.
Page renders after about 5 seconds
There is still a huge number of requests
Overview:
- Frontend performance analysis and basic tips
- Assetic to get serious about css and js optimization
- Caching
- Varnish and ESI
Enter assetic: Combine js files, combine css files

Using assetic
- Install it in Symfony 2.0, included by default in Symfony 2.1
- Configure assetic in config.yml
- Use {% javascripts %} and {% stylesheets %} to combine files
- Production: use app/console assetic:dump --env=prod to generate files
- Development environment:
- either use_controller: true in config_dev.yml
- or app/console assetic:dump --watch
Note: both will only update the file if direct source is modified. Files included by one of the files need cache:clear
Measure:
Reduced the number of requests from 62 to 9
- Only one request for CSS and JS each
- Page shows after about 4 seconds
- Older browsers only do 4 requests in parallel to the same domain, difference is much bigger there
- We won another second!
Measure:
Reduced the number of requests from 62 to 9
- Only one request for CSS and JS each
- Page shows after about 4 seconds
- Older browsers only do 4 requests in parallel to the same domain, difference is much bigger there
- We won another second!
But there are lots of image files
(a real page could have many more)
CSS Sprites
- Combine layout images into one file
- Use CSS to show right part of image
background: url('../images/sprites.png')
no-repeat -2px -43px;

CSS Sprites
- No native sprite support in assetic (yet)
- Unless you use scss/sass, where you have compass
- For the demo I built SpritesBundle for Pierre Minnieurs Sprites library
- Generate a CSS and a combined image from folders of images
- Translate image names into CSS classes to position the sprite
- Use app/console sprites:generate during deployment
- TODO: If you want to use sprites in your next project, integrate the bundle more tightly with Assetic
/* main.css */
.sprite {
display:block;
background-repeat:no-repeat;
background-image:url(/assets/images/sprite.png);
}
.logo {
width:336px;
height:63px;
}
/* generated sprites.css */
.logo {background-position:-160px 0px}
Measure: Down to 4 requests
- This will reduce the number of requests on the server
- It will make the page look nice faster
- But won't change the time until rendering starts
Measure: Down to 4 requests
- This will reduce the number of requests on the server
- It will make the page look nice faster
- But won't change the time until rendering starts
But still large files, downloaded 720 KB in total
YUI compressor
# config_prod.yml
assetic:
filters:
yui_css:
jar: "%kernel.root_dir%/Resources/java/yuicompressor.jar"
apply_to: "\.css$"
yui_js:
jar: "%kernel.root_dir%/Resources/java/yuicompressor.jar"
apply_to: "\.js$"