I'm a newbie to a lot of this stuff and we have experienced terrible performance hits with our GUI for .NET Forms, at least those with a lot of controls. My question is if there is a performance hit on the classes that you create that inherit other classes is that same performance hit in classes/objects that we don't have control of? For example the Infragistics UltraGrid instantiation?
When it get's better after loading the same Controls the second time in the same session it's the loading of the huge Infragistics assemblies. That's a one time performance hit.
Also, note that the .NET controls are being instantiated by the CLR, not the AVM, so whatever happens there is what happens to any .NET class being instantiated. Of course, I suppose there is a potential for some overhead in creating links across the bridge, but I don't know if that has been tested.
Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice http://www.cintegrity.com
tamhas wrote:Also, note that the .NET controls are being instantiated by the CLR, not the AVM, so whatever happens there is what happens to any .NET class being instantiated. Of course, I suppose there is a potential for some overhead in creating links across the bridge, but I don't know if that has been tested.
I don't know this for a fact, but I would not be surprised to learn that the ABL-CLR bridge actually introduces a performance penalty here.
Although all UI updates have to take place on the UI thread in the CLR, in C# controls and their libraries can be instantiated on background threads, as long as the UI updates happen on the UI thread.
OpenEdge has to respond to the events on the UI thread and therefore could impose restrictions on how quickly the next line of ABL code gets executed as controls are loaded.
What this means is that code that is executed on the UI thread in OpenEdge could be running significantly more slowly than the equivalent code would run if it were built in .NET only.
It would be very interesting to test this out and prove it to find what the real penalty is. I have long suspected that it may, in fact, be faster at run time to build your UI as a .NET UserControl and then host it on an OpenEdge form, rather than instantiate each control in OpenEdge and have OpenEdge handle all the interaction.
all UI updates have to take place on the UI thread in the CLR, in C#
controls and their libraries can be instantiated on background threads,
as long as the UI updates happen on the UI thread.
This is not true under certain circumstances. Trust me. I have had to debug some of the .NET UI code. If anyone tells you different they don't believe them. In most cases .NET will delay the instantiation of the actual window handle until the first time it is needed. This is the equivalent of the ABL issue of calling window:visible = true. You can make changes to the ABL object until that point, but certain changes are disallowed afterwards. Once the UI needs to be updated in .NET the CLR will allocate a handle and bind the handle to an execution context which references the thread it was created on. The execution context dictates what thread the widget gets painted on.
If you create a widget in a background thread, you run the risk of receiving a wrong execution context which means it gets bound to the wrong thread. this "non-UI" thread is not the thread that has the message pump on it. You won't run into immediate problems, but it WILL bite you eventually. If you do this and then one of the desktop events like a bit depth change, screen size change, or screen saver activation occurs, then your UI WILL hang.
Don't ever think you can get away with this safely.
Wouldn't the bridge only be a factor in slowing the *instantiation* if there was activity in the constructor which went across the bridge? To be sure, I can see an impact between instantiation and *ready for action* because of traffic across the bridge.
I think you misread what I was saying. You are absolutely correct that you cannot update a control in any way except through the UI thread, and in fact, in anything after .NET 2.0, the CLR will trap an attempt to do so and throw a runtime exception. I was not saying that controls can update the UI or invoke UI events on anything but the UI thread.
However, the term "control" is really overloaded, because controls can have both a visual and a non-visual component. Part of instantiating a control involves building a whole bunch of context around the control, much of which has absolutely no UI implication. Data controls, particularly, are completely non-ui controls, yet they are still controls.Many of them do a lot of work on non-ui threads to retrieve data, etc.
If you want an example of code that actually does this, take a look at the code on my blog at this URL:
Inside a control, you can delegate a lot of work to background threads, and as long as those controls invoke all their events on the UI thread, you are perfectly safe to use them. That, in fact, is exactly what the code in that example illustrates.
Now the same is true of controls that have large numbers of objects, like the Infragistics controls. They do a lot of work in the background on background threads and then update the UI and invoke events on the UI thread. Many of these controls rely on asynchronous callbacks to delegates that then post the events (including UI updates) on the UI thread.
The problem is that each time these events come back, they have to make their way across the bridge to the ABL so that the ABL can react to them, and this, necessarily, imposes the overhead of a call being marshaled across the bridge and invoking the AVM.
When a form is being built, the contents of the form are loaded in a single-threaded set of ABL calls across the bridge to the CLR and any callbacks that result in events in the AVM will will be deferred until the form has been completely built. While this is no different from the behavior in .NET, the additional overhead of the calls across the bridge has to have a performance impact.
tamhas wrote:Wouldn't the bridge only be a factor in slowing the *instantiation* if there was activity in the constructor which went across the bridge? To be sure, I can see an impact between instantiation and *ready for action* because of traffic across the bridge.
That's my point. Every line of code associated with instantiating a form happens as a result of a call from the constructor that initializes the controls on the form (I don't have the ABL in front of me right now, but I believe the method is called InitializeObject().
InitializeObject() contains code that instantiates each control, sets its properties, associates event handlers with events, and displays the form on the screen. Each of those lines of code results in a call across the bridge to the CLR to perform that function on a control. If any of those controls have background threads that perform any setup work that requires a response from the AVM (eg, an event handler exists on control load), the event will be deferred until all the rest of the setup has been done on the form.
Although all UI updates have to take place on the UI thread in the CLR,
in C# controls and their libraries can be instantiated on background
threads, as long as the UI updates happen on the UI thread.
My comment was about creating them on the background thread. Don't do this. It wasn't about updating them.
Yes, .NET does have checks in place to prevent you from causing screen updatets on non-UI threads, but those checks are based on the execution context which stores a reference to the handle of the thread which was active when the UI handle is created. The execution context's thread is checked against the current thread and the appropriate exception is thrown (or not).
Creating a *UI Control* (anything that allocates UI resources), *sometimes* creates that handle during the execution of the constructor. *Normally* the handle creation is delayed until after the construtor, but not always. The handle and the setting of the execution context is created when it is first needed. This was a performance enhancement (read the .net source code for comments) by Microsoft for those cases where a control is created, but never visualized. .NET buttons and tab controls do this, and some of the Infragistics controls. You have no idea of knowing which ones.
So my point is that *creating* controls on a background thread is a bad idea, because when you get around to updating them (e.g. calling BeginInvoke() or similar from a background thread) you may cause a hang.
If the control has no UI, then this is a non-issue.