Friday, August 12, 2011

Google Ads Async (asynchronous)

Making Google Ads asynchronous seems to be critical to achieving better performance, in this post in the continuing series on optimization of sites (Performance: the effect of google ads and Go Faster Stripes for site performance and google ads), a simple idea has some interesting implications.

The current ad code for Where is Croydon looks like this:

<script type="text/javascript"><!--
google_ad_client = "pub-7600935420912685";
google_ad_slot = "7690333966";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 


And performs like this:
Document CompleteFully Loaded
Load TimeFirst ByteStart RenderDOM ElementsTimeRequestsBytes InTimeRequestsBytes In
First View1.939s0.289s0.426s1871.939s1252 KB2.438s1453 KB
Repeat View1.054s0.228s0.574s1821.054s26 KB1.054s26 KB

Document complete in about 2 seconds is not good enough, (given our page has nothing on it), and the advert script is going to slow down any other asset downloads (by putting the browser into serial mode).

So how can we fix this? a bit of re-plumbing should fix this, our ad script becomes:


<script type="text/javascript"><!--
google_ad_client = "pub-7600935420912685";
google_ad_slot = "7690333966";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script> 
<script type="text/javascript"><!--
// dynamically Load Ads out-of-band
setTimeout((function ()
{
// placeholder for ads
        var eleAds = document.createElement("ads");  
        // dynamic script element
        var eleScript = document.createElement("script");  
        // remember the implementation of document.write function
        w = document.write;
        // override and replace with our version
        document.write = (function(params)
        {
// replace our placeholder with real ads
eleAds.innerHTML = params;
// put the old implementation back in place
document.write=w;
        });
        // setup the ads script element
        eleScript.setAttribute("type", "text/javascript");
        eleScript.setAttribute("src", "http://pagead2.googlesyndication.com/pagead/show_ads.js");
        // add the two elements, causing the ads script to run
        document.body.appendChild(eleAds);              
        document.body.appendChild(eleScript);           
}), 1);
                //-->
        </script> 


How does this work? Google's Ad script is quite "nasty" as it uses document.write, generally not a good idea  as it can only be used during page creation. So this script does two things:
  1. it dynamically inserts the Google ad script, allowing this to be separate from the main page render, hence the rest of the page can load without waiting for the adverts
  2. it re-plumbs the document.write function to instead add the ads to a placeholder element. If this is not done then the Google code will try to call document.write and it won't output anything as the document is already complete. For completeness the code re-plumbs document.write after the first call, but really there should be no other code calling it.
Now this code is far from bullet proof, or tested (WOMM), but it shows the basic principle - it works only because its specific to the Google code, a more generic solution might need to follow this approach.

What does all this effort yield us:
Document CompleteFully Loaded
Load TimeFirst ByteStart RenderDOM ElementsTimeRequestsBytes InTimeRequestsBytes In
First View0.484s0.332s0.584s1910.484s13 KB2.353s1355 KB
Repeat View0.347s0.211s0.611s1860.347s10 KB1.185s26 KB

Fully loaded has not changed (as expected, we are still loading the same amount), but document complete is massively improved - from 2 seconds to less than half a second. Whilst this might seem to be moving numbers around, it means that the browser can render the rest of the page before adverts, and since adverts can be very very slow, this can make the site appear much faster.

And for those of you who do want to cheat performance numbers, then changing the  "}), 1);" line to "}), 4000);" gives you this. A page that is "fully loaded" in 0.39 seconds - this is of course cheating, the adverts appear 4 seconds later, but https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/quick-start-quide#TOC-Fully-Loaded: looks for 2 seconds of no activity to judged fully loaded time.




1 comment:

Bodo said...

Hi there

I guess this change is not allowed by the google rules. :-(
I really have a problem, my ads are loading extreme slow. Means the page load time is about 3 seconds now
1,4 seconds for the page, which is okay
1.6 seconds for adsense - makes me nervous.

I recently heard that google has changed their adsense code to asyncronous, but I did not experience any change:
http://www.webmaster-source.com/2011/03/18/google-rolling-out-asynchronous-adsense-ads/

I am really thinking of using your code.