Welcome to MSDN Blogs Sign in | Join | Help

AddIn FxCop rules released! [Mueez Siddiqui]

We have released FxCop rules to verify view and contract assemblies. This should make it much easier to create views and contracts that follow the version-resilience and isolation guidelines.

Head on over to our CodePlex site on http://www.codeplex.com/clraddins to try them out. As always, we would love to hear your feedback!

Add-In Performance: What can you expect as you cross an isolation boundary and how to make it better [Jesse Kaplan]

Aren’t AppDomains too Slow?

“Aren’t AppDomains too slow?” This is one of most common questions we get when we recommend AppDomain or Process isolation to someone building an extensible application. For some applications the answer is yes, which is why we make it easy to activate without isolation as well, but for many more applications you’ll find the performance hit you take by moving to AppDomain or Process isolation is well within the acceptable range for your requirements. In this article we’re going to talk about the level of performance you can expect in different scenarios and what you can do to your application, object model, and pipeline to speed things up where necessary.  This article is all about optimizing communication once everything is up and running, but the next one is going to discuss the performances of getting everything up and running using different levels of isolation.

About the Numbers

All of the numbers I’m providing here are only designed to give you a rough idea of what to expect. They were recorded in an unscientific manner (read: on my main box with lots of other applications running) and I typically saw a range of += 10% on different test runs. The application was compiled in VS in retail mode and was run without a debugger. Nothing was NGEN’d and only the contract assembly was installed into the GAC.

Baseline for Performance Expectations

Before we get into details on the guidance we should establish the baseline of what type of performance you can expect with different types of calls across an AppDomain boundary without any interesting optimizations. For simple calls that pass and return primitive types you should see between 2000 and 7000 calls per second across an AppDomain boundary. For calls that involve passing or returning a type derived from IContract across the boundary you should see about 1000 calls per second. These numbers decrease by about 30% when you switch from an AppDomain to a Process boundary.

This means that even without any interesting optimizations you should be pretty comfortable with crossing the boundary many times in response to any sort of user input and shouldn’t have to worry about the user noticing slow-down caused by the isolation boundary.

General Guidance on Performance across Isolation Boundaries

There are two basic strategies you should utilize in reducing the impact the isolation boundary has on your application:

1.       Maximize the speed of crossing your particular boundary

2.       Reduce the number of times you cross the boundary

Maximize the Speed of Crossing the Boundary

The key insight you need to understand this bullet is that when using System.AddIn to cross isolation boundaries there are three different remoting paths that you might be taking across those boundaries:

1.       Cross Process

2.       Cross-Domain

3.       Cross-Domain FastPath

If you are going across process boundaries then you don’t have much choice on your path and your main goal, when you need to increase performance at the boundary, is to minimize the number of times you need to cross it. On the other hand if you are only using AppDomain isolation then you have more options. There is a good chance that this is the first time you have heard of the “Cross-Domain FastPath”: the FastPath isn’t exposed as an API or really documented extensively.  The FastPath boils down to a highly optimized short-cut across AppDomains which by-passes as much of the remoting stack and bookkeeping as possible. The runtime uses this short-cut whenever it can quickly prove that a given call does not require any of those remoting facilities.

There are a few things you need to do to enable the FastPath for your application, but once you enable it the runtime will choose between it and the standard cross-domain path on a per transition basis depending on the type of call you are making. Therefore your goal, if you need to increase your performance crossing an AppDomain boundary, is to first enable the FastPath for your app and then to try and stay on it for as many of the performance-critical transitions as make sense.

To enable the fast path you need to make sure that the runtime shares your Contract assembly between your host and add-in AppDomains. To do this you need to mark the main method of your exe with [LoaderOptimization(LoaderOptimization.MultiDomainHost)] and to install your Contract assembly in the GAC. The “MultiDomainHost” loader optimization tells the runtime to share all assemblies that are installed in the GAC between AppDomains.  If you have a native executable and are using the native hosting APIs to start up the runtime you can still enable “MultiDomainHost”: see these docs for more info on exactly how to do it.

Once the fast path is enabled you need to consider which calls you make will actually use it. Basically you will only stray from the fast path on calls that involve either passing or returning an IContract: IContracts take you off the FastPath because they rely on MarshalByRefObject which actually does require a hefty chunk of the remoting bookkeeping. Nearly all calls involving only serializable types will stay on the fast path.  The main restriction for keeping serializable objects on the FastPath is that they are marked serializable using the [Serializable] attribute. If you instead implement ISerializable you will end up using the normal path.  Serializable structs will also stay on the fast path and thus are a good option for passing large amounts data across the boundary when by-value semantics are appropriate.

Reducing the Number of Times you Cross the Boundary

 It is important to realize that the cost of crossing an AppDomain boundary does not grow substantially as the amount of data you pass across increases. For example, assuming no other optimizations, you can pass a byte[100000] in about twice the time it takes to pass across a byte[10]; in this case an increase in data of 10,000 times only results in a 50% slowdown. If you want to look at a MB per second measurement passing across bytes in byte[10] chunks leaves you with a rate of about 0.029 MB/second and passing them in byte[10000000] chunks leaves you with a rate of about 406 MB/second.  If you take advantage of the fast-path those numbers change to 0.75 MB/sec with byte[10] and about 560 MB/sec with byte[10000000].

Now, there are some cases where passing data in byte[] form is very natural (image processing comes to mind) but in many cases you’ll want something that is much easier to consume on the other side. In these cases passing data across in the form of structs can be very useful. Serializable structs can use the fast path across AppDomains. This, combined with the fact that they can contain large amounts of data and can even be passed across efficiently in arrays, means they are very useful in passing large amounts of strongly typed data (as opposed to byte arrays) across boundaries with a minimal impact to performance. The biggest downside to structs when compared to byte arrays is that if you are unable to enable the fast path then the performance of passing large numbers of structs degrades significantly: it is still much faster than using standard IContract calls but it may not be appropriate for passing around very large amounts of data. In our sample you’ll see that we achieve 2.1 MB/S for a struct[10] and 134 MB/S for a struct[10000000] if the fast path is enabled. Falling off the fast path leaves us at 0.085 MB/S for struct[10], 0.297 MB/S for struct[100000], and a DNF (did not finish before I gave up on it) for struct[10000000].

Strings were the last method I used for passing data across the boundary and led to some of the more interesting results: with very large strings my test bed was reporting “infinity bytes per second.” Increasing the size of the string did not increase the time it took to pass it across the AppDomain boundary regardless of whether I enabled the fast path or not. Think for a second about why that may be. As it turns out, strings are special cased by the runtime and shared across all AppDomains in the process. Thus to pass one across the boundary all the runtime needs to do is perform a pointer copy, whose speed doesn’t depend on the size of the string being pointed to. This means that if you have a system where you need to pass large amounts of text around then you’ll typically not have to worry about the size of that text when considering the performance of your AppDomain isolation boundaries.

The Data

The data below was gathered using the sample. To run the tests for yourself you’ll just need to download the sample, build it, install the contract assembly into the gac (gacutil –if [contract path]), and run the app outside of a debugger. There are comments in Program.cs as to how to modify it to test the different scenarios below. In addition to the tests below there are additional methods in the sample that you can experiment wit.

You can find the sample on our codeplex site here.

Calls/Second

Operation

Fast Path

Cross Domain

Cross Process

void DoStuff(void)

166,667

5,555

3,984

void DoStuff(int)

200,000

2,857

2,857

int GetInt()

166,667

6,666

4,016

int GetInt(int)

166,667

4,166

2,617

void DoStuff(struct)

71,428

2,777

2,358

void DoStuff(IContract)

3,205

1,098

657

 

MB/Second

Data Type

Size

Fast Path

Cross Domain

Cross Process

 

10

2.171

0.085

0.061

struct[]

100,000

85.048

0.297

0.265

 (32-bit struct)

10,000,000

134.189

DNF

DNF

 

10

0.751

0.029

0.013

byte[]

100,000

298.642

180.844

88.778

 

10,000,000

563.509

406.132

75.468

 

10

1.221

0.046

0.025

string

100,000

24.414

462.825

36.479

 

10,000,000

2,441,406.25

33,216.51

46.588

 

Bringing it all Together

We’ve just gone into a lot of detail and discussed a lot of different options for increasing performance and now that that’s out of the way I think we can distil it into a simple cheat sheet:

DO: enable sharing of assemblies using the LoaderOptimization attribute.
This is the first step to enabling the fast path. Even if you do not install your contract assembly into the GAC (and thus fully enabling the fast path) you will see an decrease in the activation time of your add-ins beacause this allows the Framework assemblies to be shared between domains.

DO: install the Contract assembly in the GAC when using AppDomain isolation.
This step along with the LoaderOptimization attribute will enable the fast path for your application and will provide dramatic performance increases for many of your calls across the AppDomain boundary. If you are exclusively using Process isolation this is not important.
Note: this step means that your installation program will require admin privileges. 

Consider:  passing data across boundaries using by-value semantics (structs, byte[],strings, ect).
 
If you are passing large amounts of data between your host and add-in you should consider passing it across in one chunk using a by-value data-type rather than in pieces using a deep IContract based hierarchy. This is approach is very natural in cases where an add-in needs to process and possibly modify the data:  just pass it out to the add-in and have the add-in return the modified set. It can be more awkward if the data is dynamic and can change continuously but using a system of events to notify the data consumers of changes can help mitigate this.

DO NOT: dismiss an isolation level without first giving it a try in the scenarios you care about.
Many times you’ll find that in a real application the performance hit you take by moving to an isolation boundary isn’t as much as you expect and perfectly acceptable for your situation. In the cases where it is not, there are a few simple things you can do to make the experience better.

One of the things I hope you get from this article is that for many extensible applications an AppDomain boundary, or even process, boundary is just fine without any performance tweaking and that for those applications that need (or want) better performance there is a range of options for providing dramatic performance increases depending on your needs.

 

 

Posted by CLR Add-In Team | 5 Comments
Filed under: ,

Feb CTP of Add-In Pipeline Generator is Released on Codeplex

We've just release the first update to our Pipeline Generator to Codeplex: https://www.codeplex.com/Release/ProjectReleases.aspx?ProjectName=clraddins&ReleaseId=9222

This release addresses many of the issues/suggestions the community filed with the first release. Additionally it adds support for defining structs in your contract assemblies. For some extensibility points structs will be an invaluable tool as they allow you to pass large amounts of data across the AppDomain/Process boundary much faster than you can with reference objects.

For a complete list of changes see the release notes in the linked project.

Announcement: New Tool to Auto-Generate Add-In Pipelines [Jesse Kaplan]

This has been one of the most requested features we've had since people started learning more about our model and playing with the bits and I'm very happy to announce that we have published this on CodePlex: http://www.codeplex.com/clraddins/Wiki/View.aspx?title=Pipeline%20Builder

For a particular version of an application writing the code for the views and adapters can be a lot of mechanical repetitious work and generally only gets interesting when you are writing "cross-version" adapters. So what we've done is build a tool that will automatically generate the source code for the views and adapters for a given contract assembly. This tool is really just a library that can be used from various other places and our release includes a simple command line tool as well as a VS 2008 add-in that will automatically generate the projects and set references/build paths for you appropriately.

This tool should still be considered in pre-release state but because of the nature of it that shouldn't discourage you from using it in your products. While we are still working on adding additional features (and will be taking requests/suggestions for them), as long as tool has the features you need you should feel comfortable using it. Since the code this tool generates doesn't take any dependencies on the tool, or related assemblies, new updates will not impact your app and should be easy enough to incorporate in your build process. If we post updates that change the code generated for currently available features we'll make sure that gets noted and describe the changes.

One of the nice things about releasing this as shared source rather than as part of an official release is that we'll be able to do continuous development work on this and can release updates frequently, so please give it a spin and if you have feature requests/bug reports please send them our way.

 

In addition to the PipelineBuilder tool we will be using our team CodePlex site (http://www.codeplex.com/clraddins) to release all samples from now on. Many of you have noticed that because of changes to our API between betas and CTPs of 3.5 some of our existing samples no longer compile. We've posted updated versions of all of these samples on CodePlex and have added links to the them from the original posts.


 

Posted by CLR Add-In Team | 1 Comments
Filed under: ,

Support for Windows Forms in Hosts and Add-Ins

We’ve been receiving a lot of questions recently regarding the support for Windows Forms based add-ins and hosts in a multi-AppDomain environment. Now that we’ve released .NetFX 3.5 with the support for passing WPF controls across AppDomains the lack of direct support from Windows Forms becomes more noticiable.

The good news is that it’s actually possible to use Windows Forms controls in both your hosts and add-ins and have them contribute directly to each others UI. The bad news is that Windows Forms, currently, doesn’t have any dirrect support for this feature and so you’ll have to go through WPF to do this.

In 3.5 the WPF team created the helper functions, in the System.AddIn.Pipeline.FrameworkElementAdapters class, which will convert to/from FrameworkElements and InativeHandleContract. It does this by wrapping the FrameworkElements in an HWND on one side and then parenting this HWND to a FrameworkElement on the other. This means that in actuality the FramworkElement and its children never actually get passed across the bounary.

If, in addition to the FrameworkElementAdapters class, you use the WindowsFormsHost and ElementHost classes to host WindowsForms controls in WPF elements and vice-versa then both your hosts and your add-ins can use WindowsForms controls and you can logically pass them across the AppDomain boundary without actually doing so. The downside to this approach is that this requires that you load WPF into your process. If you already have some WPF code running in your process then WPF requirment is very minimal, but it can be significant overhead in the case of a pure WindowsForms host with pure WindowsForms add-ins and you’ll have to decide for yourself whether it is worth it.

There were also a few lingering questions about whether the above technique was even supported by the WindowsForms teams but there is good news there. We were recently able to clarify this question and the answer is that it is supported because it manages to avoid actually passing across and WindowsForms controls across the AppDomain boundaries: http://msdn2.microsoft.com/en-us/library/8bxxy49h.aspx

 

 

Posted by CLR Add-In Team | 14 Comments
Filed under:

Video: Jack Gudenkauf on the BCL and Add-ins [Jack Gudenkauf]

I was recently interviewed by James Vastbinder, an ISV Platform Strategy Advisor for Microsoft.

We discussed the needs of ISV’s and what’s new in the pending Orcas release (i.e., CLR v3.5 and Visual Studio 2008 v9.0).

And for those interested in hearing more on Application extensibility and the Add-in (Add in) model, including some newly released features, like WPF support, check out the following link.

 

Note the disclaimer on WinForms and WPF utilizing Crossbow over the CLR Add-In model.

Here is a bit more on this subject:

 

The use of "Crossbow" for WPF and WinForms interoperability has been a frequently asked question.

In Orcas, we haven't thoroughly tested Crossbow scenarios utilizing the WPF Add-In offering.

That said, WinForms applications with multiple AppDomains are supported as long as no WinForms controls are passed across the boundary.  In the WPF Add-in implementation we do not pass Add-in controls across domains, therefore, the use of "Crossbow" *should* work.  Your mileage may vary :-)

AppDomain Isolated WPF Add-Ins [Jesse Kaplan]

Earlier Jack announced an exciting new feature in our beta2 release in the ability host WPF controls across AppDomain boundaries. This will allow add-ins to generate their own UI and to pass it back for display within the hosts window. Ever since the add-in model was concieved this has been near the top of our list for feature requests and I want to thank the WPF team for scrambling to get this work done in time for the 3.5 release.

With that in mind I'd like to finally update our calculator samples to be just a bit more interesting. The attached sample requires Orcas Beta 2 and demonstrates a fairly typical add-in scenario in which an AppDomain isolated add-in generates some UI at the request of the host and the host displays directly as part of the application. If you take a look at the screenshot below the lower-right hand corner of the application is the piece that the add-in is generating. Everything else is generated by the host either on its own or using data from its various add-ins. In this sample only one section of the host actually displays the add-ins content but there is nothing limiting that in the product: a host can display multiple FrameworkElements from one or more add-ins anywhere it wishes. 

Calculator Screen Shot

When you download the application you should play around with the "Graphing Calculator" add-in. It really shows how smooth the integration between the AppDomains are in that the add-in doesn't need to mearly display a static image but can accept rich interaction directly from the user even when hosted cross domain.

We'll go into more depth later about this technology, some of the scenarios it enables, and some of the gotcha's you might run into, but for now I'd like to just get the sample into your hands and give you a brief taste of how easy it is to integrate this into your add-in application by comparing the differences between the pipelines of the old calculator and the new graphical add-ins.

First let's take a look at the views:

Numerical Calculator:

    [AddInBase]
    public abstract class Calculator
    {
        public abstract String Name
        {
            get;
        }
        public abstract IList<Operation> Operations
        {
            get;
        }
       public abstract double Operate(Operation op, double[] operands);
    } 

Graphic Calculator:

    [AddInBase]
    public abstract class VisualCalculator
    {
        public abstract String Name
        {
            get;
        }
        public abstract IList<Operation> Operations
        {
            get;
        }
        public abstract UIElement Operate(Operation op, double[] operands);
    }

Only one line has changed, instead of returning a double the view simply returns the basic WPF control UIElement. The changes to the host view are just as simple.

Now let's look at the adapters as this is where the real magic happens. This time we'll just compare the pieces of code that have changed.

 On the add-in side:

        public double Operate(Calculator.Contracts.IOperationContract op, double[] operands)
        {
           return _view.Operate(OperationViewToContractAddInAdapter.ContractToViewAdapter(op), operands);
        }

        public INativeHandleContract Operate(Calculator.Contracts.IOperationContract op, double[] operands)
        {
            return VisualAdapters.ViewToContractAdapter(_view.Operate(OperationViewToContractAddInAdapter.ContractToViewAdapter(op), operands));
        }

The only changes here is that instead of passing back the result directly across the boundary as a double we now pass it back across as an INativeHandleContract and use the new class VisualAdapters to convert from the UIElement the add-in passes back into the INativeHandleContract that gets passed across the boundary. System.AddIn.Pipeline.VisualAdapters is a new class in the new System.Windows.Presentation assembly that contains the core functionality that enables the cross-appdomain hosting; you'll see it's use again in the host side adapters to convert from the INativeHandleContract back into a WPF control.

        public override double Operate(HostView.Operation op, double[] operands)
        {
            return _contract.Operate(OperationHostAdapters.ViewToContractAdapter(op), operands);
        } 

        public override UIElement Operate(HostView.Operation op, double[] operands)
        {
            return VisualAdapters.ContractToViewAdapter(_contract.Operate(OperationHostAdapters.ViewToContractAdapter(op), operands));
        } 

Again, the only changes required were to change the return value to UIElement and to use our new helper class to convert from the contract back into a a new UIElement. This method actually returns an object of type FrameworkElement that lives in the hosts AppDomain and can be manipulated and plugged into the visual tree as any local FrameworkElement can.

That should be enough to get everyone started, as I said we'll be following up with more info and samples in the weeks to come. If you have any questions about this new capability please just let us know.

 

Note: The attached sample was built for a pre-RTM version of .NetFX 3.5 and will not work on the RTM build. For an updated sample please see our codeplex site here: https://www.codeplex.com/Release/ProjectReleases.aspx?ProjectName=clraddins&ReleaseId=9454

 

Posted by CLR Add-In Team | 33 Comments
Filed under:

Attachment(s): AppDomain Isolated WPF Demo.zip

By Popular Demand [Jack Gudenkauf]

We just released VS 2008 and .Net 3.5 Beta 2 (code name "Orcas").

You may download the release here.

 

What’s new?

 

1.       UI extensibility

By far the most requested feature has been UI extensibility built upon the Add-In model.

We are very happy to announce that we have delivered a solution!

We have added a Native Handle Contract (in System.AddIn.Contract) upon which, the WPF and CLR Add-In teams have built an implementation for enabling an application to Host WPF Add-In’s!

We will be posting more on the subject including a sample(s) showing isolated (i.e., Cross AppDomain) WPF Add-In’s.

Special Thanks to the WPF team for making this happen!

 

2.       Direct Connect Optimization

We have provided an optimization to Add-In activation.

For Hosts and Add-in’s having the same view of each other (e.g., in V1) that forgo unloadability and share the same security context (i.e., run in the same AppDomain), you can choose to optimize the Add-In loading behavior (AddInToken.EnableDirectConnect) by not instantiating the Adapters.

 

3.       Custom Qualification

We now support the ability to add a custom attribute (i.e., QualificationData) to your Add-In and/or Pipeline segments.

There are a number of clever things you could do with this ability.  You could provide your own custom qualification logic for things such as Add-in’s that use this Attribute to inform you they qualify themselves as only running in the default AppDomain, etc…

The attribute data is available via the AddInToken.

 

4.       Find Add-In

We have added a new method (AddInStore.FindAddIn()) that enables you to find an Add-In given a fully qualified path and type.

Among other uses, this feature allows you to forgo the demand for an Add-In Attribute used for Discovery.

This feature also supports scenarios where you may not know the Add-In location until Run Time (e.g., Add-In’s deployed via Click-Once).

 

5.       Passing and Re-Adapting Add-In’s (ContractAdapter)

One of the many benefits of the Adapter model is the ability to Re-Adapt.  Sharing Add-In’s without the demand  to Discover and instantiate a new instance is a powerful capability.

We provide facilities to pass the Add-in around and let each consumer of it choose its own view and let the system find the appropriate adapter between the activated add-in and the requested view.

 

6.       Add-in Activation Security enum

We have added a new enum value for activating the Add-in using the Hosts Security level (i.e., AddInSecurityLevel.Host).

 

7.       View Hierarchies

We now support a hierarchy of view types and the ability to attribute them for discovery.

 

We will be posting more on these features in the coming months.  Stay tuned.
Posted by CLR Add-In Team |