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 http://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 RegOpenKey each 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 (34)

  1. orz says:

    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?

  2. simple fix says:

    As you noted in your comments:

    "since add-ons execute in the context of the UI thread"

    Well, this is an easy fix!

    When initializing the addon, if there is an update to be made, a new thread should be spawned…

    A "readyState" flag on the addon should be updated when the updates are complete, but until they are done, either run the addon in the last stable version, or disable it until the addon is updated.

    Or finally, let the developer choose when they want to do the update, and have it run on demand…

    e.g. User loads google, they don’t need an update,… user loads page with the "ACME services running", a check is done for updates… if found, informs user of updates, and gives _THE_USER_ the choice as to when to update.

  3. Chad says:

    Off topic, but ….

    Could you please remove the horrific clicking sound every time you click a link? that’d be great.

    thanks

  4. EricLaw [MSFT] says:

    @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.

  5. 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.

  6. 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.

  7. EricLaw [MSFT] says:

    @Ben, you can either copy/paste the full URL, or use this one:

    http://msdn2.microsoft.com/en-us/library/ms537169.aspx

  8. Amy says:

    Ok this is a bit offtopic but can one of you IE developers tell me how i can get the natural width and height of an image? This should be easy using C++ and COM? Is there some QI i can do to get this information? I am using a really slow hack for this and its is the main reason my Add-on is slow.

  9. user says:

    When IE (7 and 8) starts up, e.g. before  the menu bar appear, if I paste some words to the URL box or search box, the final text shown in these boxes will be concated with the words I pasted and the original text/"grey google" text.

    E.g. I paste abc into search box during start up, which using Google as the seacher. The  text becomes "abcGoogle"

    I am using WinXP ENU SP2

  10. Rob Parsons says:

    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.

  11. Rob Parsons says:

    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.

  12. Tino Zijdel says:

    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.

  13. Joe says:

    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.

  14. anonymous says:

    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.

  15. IEdude says:

    Add-on is cool, more ActiveX(MS Internet Control) support is better !

  16. Assaf says:

    The current add-on model for OS components (for example IE and explorer), that is native code running in the context of the host is outdated and no longer fits the requirements of the Internet era.

    The problems with these add-ons are:

    - An add-on has the same security restrictions as the host – that is, almost no restrictions. He can write files, change registry settings, etc..

    - As noted above, an add-on can affect performance of the host

    - And most important, no accountability – the add-on can change host behaviour without being accountable – you don’t know which extension or add-on creates a specific behaviour, problem, performance degredation, etc…

    This model fits if the add-ons are all written by the host creator, or if the host isn’t some critical component, but on critical components such as the operating system’s browser or file manager, nothing should be allowed to disrupt the usual operation, and anything added should be easily removed.

    I’ve stumbled upon a Microsoft Research project, Singularity, which tries to address some of these problems. It’s an interesting concept, but it’ll probably take many years to implement this approach fully.

  17. Joe says:

    @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).  

  18. News says:

    As we worked towards the recent release of Internet Explorer 8 Beta 1, the IE team focused hard on performance

  19. demon says:

    @chad – i like that clicking sound

    @anyone else – all i know is, ie 8 looks and feels just about the same as ie 7.  granted, i hardly used it cuz everytime i went to utube, it would serve me up a lovely fatal error and crash.  ie 8 def has some flash issues, at least on my end.

  20. Tino Zijdel says:

    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.

  21. nexalencype says:

    Продается дом в поселке Николькое. 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 Алексей

  22. Mike Dimmick says:

    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).

  23. Phil says:

    "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.

  24. Anonymous says:

    @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.

  25. Eli says:

    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.

  26. Clinton Gallagher says:

    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.

  27. ping says:

    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

  28. Confused says:

    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.

  29. Geld Lenen says:

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

  30. IEBlog says:

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

  31. IEBlog says:

    I’m Christian Stockwell, a Program Manager on the IE team focused on browser performance. Measuring the

  32. Проблемы при оценке производительности браузеров Добрый день! Меня зовут Кристиан Стоквелл (Christian

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

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