Designing for Add-on Performance

As we worked towards the recent release of Internet Explorer 8 Beta 1, the IE team focused hard on performance. As part of our effort to improve IE, our investigations have revealed several add-on performance problems. In this post, I want to share some of the common themes that we have discovered.

First, I would like to thank those of you who have provided feedback on this blog, in the IE Beta NewsGroup, and around the web. The Internet Explorer team has been working hard on performance in IE8 and it is great to see the results of some of our early investments. We still have room (and plans) to improve, but for now you can find out more about the performance improvements in IE8 Beta1 from our developer whitepapers.

If you are new to the world of developing IE add-ons and want some background material, here are a few great links to get you started:

Broadly speaking add-on performance issues typically impact IE users in two areas:

  1. Opening/Closing the IE window or individual tabs
  2. Browser responsiveness

Opening and closing speeds are largely impacted by add-ons performing lots of expensive work every time they are created. One particularly common problem is that add-ons check for updates during either browser startup or shutdown.

Registry misuse has been a common problem leading to poor responsiveness. Many add-ons perform expensive registry operations that can reduce Internet Explorer’s responsiveness.

In the sections below I discuss these two areas and provide some guidance for designing performance into add-ons.

Add-on Initialization and Checking for Updates

  • Principle 1: Be lazy – give hard work to another thread
  • Principle 2: Don’t pay a toll every time you start the car

During startup, Internet Explorer checks the registry for installed add-ons. When IE detects an installed Browser Helper Object (BHO) or toolbar it calls CoCreateInstance to instantiate each installed and enabled add-on. Essentially, Internet Explorer creates add-ons as inproc servers, executing in the context of IE’s main UI thread. For backwards-compatibility Internet Explorer follows these steps for every opened tab. This behavior is important for several reasons, and you’ll see why as I discuss some of the most popular problems encountered by add-ons.

Be lazy – give hard work to another thread

One common trend in many of the popular add-ons today is integration with online content. Maintaining this integration with live data invariably entails some update mechanism. In many of the cases we have investigated, add-ons perform synchronous update checks when IE hands control over to the add-on’s SetSite implementation during initialization.

From my description of how add-ons are initialized in Internet Explorer, you can guess what the potential impact is from these types of update checks. Consider the following flow:

  1. IE begins initialization
  2. IE detects that the Foo Toolbar has been installed
  3. IE calls the Foo Toolbar’s SetSite method
  4. Foo Toolbar contacts https://foo.example.com to check for updated content
  5. Foo Toolbar returns control to IE
  6. IE continues initialization and displays the user’s homepage

See the problem yet? Consider step 4 above – what happens if the Foo Toolbar finds lots of content that needs to be updated, if the user’s connection to the content server is slow, or if the user is working offline? The answer is, (since add-ons execute in the context of the UI thread), that the toolbar can cause IE to become unresponsive for long periods of time or can lead to IE’s startup and shutdown times inflating faster than a balloon at a clown convention.

A better approach is to create a worker thread that can perform the content update asynchronously. The preferred way is to use SHCreateThread (when developing an add-on in C++) as follows:

STDMETHODIMP SetSite(IUnknown* pUnkSite)
{

if (pUnkSite != NULL && IsUpdateRequired())
{
        SHCreateThread(Update, NULL, CTF_COINIT | CTF_PROCESS_REF, NULL);
}
else
{
         // Release cached pointers and other resources here.
}
 // Return the base class implementation
return IObjectWithSiteImpl<CHelloWorldBHO>::SetSite(pUnkSite);

}
DWORD WINAPI Update(LPVOID pParam)
{
            DWORD dw = 1;
            // Perform update here
           return dw;

}

DWORD WINAPI IsUpdateRequired()
{
           DWORD dw = 1;
            // Perform a low-cost check here to verify that an update should be
// performed. This can be accomplished by checking a registry key.
           return dw;

}

Notice that in the above example SetSite creates a new thread to execute the Update method. Using this approach SetSite does not run the risk of blocking the UI thread for extended periods of time, and the add-on is still able to update its content. Also notice that by establishing a suitable frequency for update checks (for example, every 2 or 3 days) add-ons can be updated quickly without forcing users to pay the price of the update check with every browser or tab opening.

Adopting this approach can help move long-running operations off of IE’s main UI thread and can lead to better perceived performance. It is important to remember, however, that moving to a worker thread is not a panacea. There are many potential issues, including the possibility that numerous expensive cross-thread COM calls could outweigh the benefit of moving to a worker thread.

Pay the toll when you get to the booth

Handing off long-running operations to a worker thread helps avoid UI hangs. Nevertheless, users may still pay an avoidable up-front cost every time your add-on is initialized. Users often start IE without taking advantage of the updated content. In these cases both the users and content providers are paying extra costs associated with the update checks without any commensurate dividend.

When performing content updates an extreme approach would be to pay the costs only when users have explicitly announced that they want new content – by clicking on the “Check for Updates” menu item, for example. That solution is, however, unrealistic in many cases because it could compromise the add-on’s performance. For example, consider a user clicking on a drop-down menu, and having to wait a second to view the associated drop-down while updated content is downloaded – yikes!

There are a variety of techniques that more effectively balance user experience and up-front costs. For example, toolbar developers might want to consider moving their update checks out of SetSite entirely and do them either the first time the user mouses over the toolbar, or update on a fixed schedule. Exact solutions will vary from add-on to add-on, so it’s important to stay creative and try to avoid forcing fixed costs on users whenever possible.

In almost every case there is a way to avoid doing lots of work in either SetSite or in an OnDocumentComplete handler. Taking the extra time to push work out of these areas is a great way to avoid performance problems and ensure that users are happy to install your add-on.

Using the Registry

  • Principle 3: Caching is your friend
  • Principle 4: Break the habit – Don’t flush!
Caching is your friend

Using the registry is sometimes reminiscent of the Macarena circa 1996 – a few people knew the steps, fewer people were actually good at it, but neither of those facts prevented everyone else from taking part. Registry overuse is common among Windows applications, and we have been working hard to reduce our registry accesses with IE8.

Overusing the registry is discouraged because the overhead of registry operations can be significant – opening, reading, and closing a cached key can cost tens of thousands of cycles. Since it is relatively common for individual add-ons to perform hundreds, thousands, or even tens of thousands of registry accesses during startup, these costs can quickly add up to a noticeably slower browser.

Fortunately, it is possible to reduce the cost of using the registry. First and foremost, optimize for the common case. It is very likely that most registry values are not going to be changed during the course of an add-on’s execution, so reading the value once and then maintaining a cache can significantly reduce the number of individual registry accesses.

Where it is not possible to eliminate registry accesses, you can often reduce the cost of the remaining operations. It turns out that accessing keys using full registry paths (e.g. HKEY_LOCAL_MACHINEFooBar) can be two to three times as expensive as using relative paths, depending on number of levels separating the target key from the provided root. Add-ons typically have the vast majority of their settings available under a key or a small set of keys. For example, suppose an add-on wanted to retrieve the associations used by IE. The following registry keys would need to be accessed (under HKEY_LOCAL_MACHINE):

SOFTWAREMicrosoftInternet ExplorerCapabilitiesFileAssociations SOFTWAREMicrosoftInternet ExplorerCapabilitiesMIMEAssociations SOFTWAREMicrosoftInternet ExplorerCapabilitiesUrlAssociations

Using the Win32 method RegOpenKeyeach of the regkeys could be accessed with the following snippet of code (using FileAssociations as an example):

HKEY hk;

RegOpenKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\Microsoft\Internet Explorer\Capabilities\FileAssociations", &hk);

The remaining keys could be accessed in a similar fashion using HKEY_LOCAL_MACHINE as the root. However, a better approach in these cases is to create a handle to the Capabilities key and then perform additional relative-path RegOpenKey operations to retrieve the remaining values, as follows (again, using FileAssociations as an example):

HKEY hkRoot;

RegOpenKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\Microsoft\Internet Explorer\Capabilities", &hkRoot);

HKEY hkFileAssoc;
RegOpenKey(hkRoot, L"FileAssociations", &hkFileAssoc);

Break the habit - Don’t flush!

Lastly, in the past we have seen add-ons using the RegFlushKey to ensure that their registry values were in fact pushed out to disk. In some cases this is done in an attempt to maintain state between two instances of an add-on running in separate tabs or windows.

As noted in the MSDN documentation for RegFlushKey, there is rarely a need to use this API. Furthermore, calling RegFlushKey can be surprisingly expensive as it will ensure that all the data backing the registry has been written to disk. That activity may take hundreds of milliseconds to return control to the calling program. Even worse, accesses to the registry will be blocked while it completes.

As a result, calls to RegFlushKey can have an impact not only on IE but can reduce performance throughout the system. Rather than flushing the registry, add-ons using the registry for synchronization between instances can use RegNotifyChangeKeyValue to maintain state. Larry Osterman and Raymond Chen have blog posts on (mis)use of the registry that are worth reading for more detail:

I hope my guidelines on improving add-on performance help you understand some of the common problem areas we have encountered. Thanks for contributing great add-ons to the Internet Explorer ecosystem, and I look forward to your comments.

Christian Stockwell
Program Manager
Performance Geek

Edit: Added "Root" to this line of code: RegOpenKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\Microsoft\Internet Explorer\Capabilities", &hkRoot);

Comments

  • Anonymous
    April 04, 2008
    Very interesting, but totally fails compared to FUEL. Why are we mucking around with the registry at all? Can't this be a simpler API?

  • Anonymous
    April 04, 2008
    The comment has been removed

  • Anonymous
    April 04, 2008
    Off topic, but .... Could you please remove the horrific clicking sound every time you click a link? that'd be great. thanks

  • Anonymous
    April 04, 2008
    @Chad: For other applications that host the WebOC, simply use FEATURE_DISABLE_NAVIGATION_SOUNDS http://msdn2.microsoft.com/en-us/library/ms537169(VS.85).aspx For IE, just use START> CONTROL> Sounds>Sounds> SoundEvents and change the "Start Navigation" sound to blank.

  • Anonymous
    April 04, 2008
    Great to see this level of detail on IE Blog, and with links to even more detail. :) EricLaw, your link didn't work. Likely reason is an overly cautious comment parser.

  • Anonymous
    April 04, 2008
    I guess there will be much more work in future to make the addons creation and optimization easier. Because now it doesn't look very attractive. At least to me. Anyways, thanks for sharing some helpful information.

  • Anonymous
    April 05, 2008
    @Ben, you can either copy/paste the full URL, or use this one: http://msdn2.microsoft.com/en-us/library/ms537169.aspx

  • Anonymous
    April 05, 2008
    The comment has been removed

  • Anonymous
    April 05, 2008
    The comment has been removed

  • Anonymous
    April 05, 2008
    Calling home for updates should be a user-iniated process that is accessed by clicking a button and not as a setting option as in the Google Toolbar.

  • Anonymous
    April 05, 2008
    The Google, Yahoo and Live toolbars all over-write the IE7ToolbarLayout in the registry on startup and negate user preferences. The Google and Yahoo toolbars can be fixed by disabling the Satellite BHO with the Add-ons Manager, but the Live Toolbar refuses to honour User Preferences. Google Beta 5 has corrected this problem, but of coarse without auto-matic updates many users are left in confusion. It would be prudent to advise these Toolbar vendors directly about Best Practices as Public Feedback options do not cater for Technical explanations. Please pass your recommendations on to the Live, Yahoo and Google toolbar developers.

  • Anonymous
    April 05, 2008
    This blogpost merely illustrates that creating add-ons for IE is far to complex. Basically a good API should take care of such hassle as having to deal with update-checking, registry-storage etcetera. Also there should be a simple, light-weight add-on model where normal scripting can be used to perform simple tasks in/on opened windows/tabs. If IE would have had such light-weight scripting-based add-on model we would have had good and helpful webdevelopment tools already years ago and it would certainly have helpt the adoption of IE as a serieus development platform.

  • Anonymous
    April 05, 2008
    Tino, as described AT LENGTH here and elsewhere, there are many ways to write IE extensions, including Javascript. See, for instance,  http://blogs.msdn.com/ie/archive/2005/09/06/461675.aspx Part of the problem with powerful extension models is that careless or sloppy addon developers will misuse them.  This post is an attempt to help educate those developers who choose to write native code extensions.

  • Anonymous
    April 05, 2008
    Speaking of add-ons, one of the most horrible addons that nearly kills IE performance has been the Live Search Club Toolbar. While I know that comes from a different team at MS, it would be really nice if you guys could impart and share your knowledge also with those ppl. No other addon I've known slows down IE as much as the Live Search Club toolbar.

  • Anonymous
    April 05, 2008
    Add-on is cool, more ActiveX(MS Internet Control) support is better !

  • Anonymous
    April 05, 2008
    The comment has been removed

  • Anonymous
    April 06, 2008
    @assaf: It's a nice idea, but unfortunately, pretty unworkable.  Users prefer to have powerful extensibility available within their browsers, and any artificial restrictions which are introduced more than likely will compel the user to use another browser which offers them more power. Singularity is a nice toy, but Joel Spolsky has pointed out the problems with sandboxes pretty nicely (e.g. here: http://www.joelonsoftware.com/items/2007/09/18.html).  

  • Anonymous
    April 06, 2008
    The comment has been removed

  • Anonymous
    April 06, 2008
    The comment has been removed

  • Anonymous
    April 06, 2008
    Joe: if you have ever tried to create an extension that is just a bit more non-trivial than adding a context-menu item, like adding a toolbar or sidepane that can interact with the current loaded document and receives events on navigation, you would know that it is just not that simple. Even the most trivial examples require fiddling in the registry and thus are a pain for the writer to create and distribute and for the user to install and administer. For instance: I have (JS-based) code that can be used to 'live edit' CSS on any webpage that works in IE and would love to make an add-on out of it with a toolbar and a sidepane that shows the CSS. Fact is that that last part is pretty hard to achieve when you've got no experience in that kind of programming and from what I've seen in MSDN documentation and examples it is quite a steep learning-curve to master that kind of programming. So basically it requires me to learn special skills that I otherwise have no actual use for or I would have to outsource that particular part which makes it more difficult to provide my add-on for free and also maintain it myself.

  • Anonymous
    April 06, 2008
    Продается дом в поселке Николькое. 2км от МКАД по Горьковскому шоссе. Дом 400 метров. Участок 10 соток. Ландшафтный дизайн. Все коммуникации, отопление. Гостевой дом. Дом строился "Под себя" подробнее: [url=http://realty.skeeve.ru/sell/nikolskoe.html]http://realty.skeeve.ru/sell/nikolskoe.html[/url] $700 000. 8-926-203-77-81 Алексей

  • Anonymous
    April 06, 2008
    On the subject of the registry, I'd just like to add that the guidance to reduce registry use does not mean you should start doing everything in configuration files instead. The registry is many times more efficient than a text-based configuration file, for small to medium sizes of data. The people using RegFlushKey for synchronising different instances of the same control are WRONG WRONG WRONG because the OS does not need to go back to the disk. Registry updates in one process are immediately seen in all other processes. The OS caches recently-used parts of the registry data in physical memory. The cost of opening a key and reading a value comes from the fact that the registry API is down in kernel mode and a kernel mode transition takes millions of cycles. It takes longer to open a key with a longer key path because it has to start at the top of the tree and follow pointers all the way down (some of which may not be in memory already and need to be paged in from disk). It also has to check security every time you open a key, so keeping keys open can be a good idea. If you have many values you need to check in the same key, and you know their names, you can use RegQueryMultipleValues to retrieve them all at once. If you can, structure your registry storage to have lots of values under one key, not lots of keys with few values. If you really want to share data between different instances of the same control in different processes, consider using shared memory (CreateFileMapping with the first parameter INVALID_HANDLE_VALUE, to get a pagefile-backed area, and naming the object so you can repeat the process for each one, then MapViewOfFile to actually access it).

  • Anonymous
    April 07, 2008
    "The registry is many times more efficient than a text-based configuration file, for small to medium sizes of data." I'd disagree.  How many people have trashed their Windows installs due to altering the wrong key somehow or making a typo?  If you mess up a separate config file, you've only hosed that app. The Registry is a horrendous mess of keys and values.  A new vision needs to be created for future Windows - Registry is simply too unwieldy and cumbersome.

  • Anonymous
    April 07, 2008
    @Phil:  You aren't really disagreeing with what he said, you're making an entirely different point. "A is more efficient than B" doesn't necessarily mean "A is better than B overall".  Although it does mean that "B is not better than A in every aspect". We're not talking about manually editing the registry anyway.

  • Anonymous
    April 08, 2008
    Windows has lots of low-level optimizations for making the registry as fast as possible and keeps most of it in RAM, unfortunately. This is mainly because lots of third party apps think accessing the registry is free. It probably is way more effecient than reading a text configuration file, but that shouldn't matter because you're supposed to be caching your settings either way. :) There's lots of advantages to having a registry, if there's issues they should be fixed as opposed to throwing the whole thing out.

  • Anonymous
    April 08, 2008
    The use of Task Panes in IE has been under utilized not to mention hardly documented at all. An href can for example target="_search" and load a web form in a Task Pane that will open on the left side of IE. At the moment IE8 does is not backward compatible and does not support _search Task Pane which I have used often and hope to encourage its further development and documentation. In fact I think the Task Pane should become a significant aspect of IE extensibility --BUT-- at least don't break what's already supported.

  • Anonymous
    April 09, 2008
    disabling and enabling add-ons requires IE restart. Would be nice to not restart IE everytime we want to disable add-ons and IE user can easily turn it on without the Manage add-on UI showing up. If that is possible

  • Anonymous
    April 10, 2008
    With regard to the add-on update check, why don't you recommend just using version numbers for add-ons? Even with asynchronous update check, based on the way you describe it, it'll slow down the connection for a while. A two or three byte version code would be so much faster to check, and could be done either way without causing a noticeable slow down (assuming the person doesn't have an excessive amount of add-ons). After finding a newer version, then give the user the option of "updating now" or later with an add-on management button (opens management window which lists add-ons, green for up-to-date, red for out-of-date, and has check boxes for the red ones with an "update selected add-ons" button somewhere visible in the management window). This would let people with slow connections update later when they're not going to need to be online and doing things. As far as I'm aware not even FireFox has such a usable add-on management system, this could be an area you could surpass them in right now. But eh, you guys can take the advice or leave it, makes little difference to me.

  • Anonymous
    April 11, 2008
    This sure will speed things up, like Steve Souders mentioned in his post. Keep up the great work!

  • Anonymous
    December 02, 2008
    A few of the startups building browser Add-Ons have organized the first ever Add-On Con , to take place

  • Anonymous
    January 23, 2009
    I’m Christian Stockwell, a Program Manager on the IE team focused on browser performance. Measuring the

  • Anonymous
    February 02, 2009
    Проблемы при оценке производительности браузеров Добрый день! Меня зовут Кристиан Стоквелл (Christian

  • Anonymous
    February 02, 2009
    Добрый день! Меня зовут Кристиан Стоквелл (Christian Stockwell) и я возглавляю команду разработчиков

  • Anonymous
    March 19, 2009
    &#160; 최근 Internet Explorer 8 Beta 1 출시를 위해 개발중인 IE 팀은 성능에 심혈을 기울이고 있습니다. IE&#160; 향상을 위한 노력의 일환으로 실행한