Jump to content

Extremely Long Load Time with LVOOP, SuPanels, and VI Templates


Recommended Posts

I am designing a large application which utilizes LVOOP to read and write "signal" objects which translate to physical hardware commands. A requirement of the application is to provide an interface panel that can load different types of signals (Integer, Boolean, Enumerated, ...). Multiple interface panels can be created (re-entrant), and each interface panel may contain one or more signal objects. These requirements have led me down the path of creating a re-entrant interface panel that contains SubPanels for signal display and control.

I created a VI for each signal type to display in the SubPanel. Since multiple signals of the same type can be simultaneously displayed, I was required to create a new VI from a "template" in order to load within the SubPanels.

The problem is that each SubPanel Signal (template) has an extremely long load time (45-60 seconds) due to the use of LVOOP classes. Since LVOOP requires the entire class to be loaded into memory and this is a large project, hundreds and hundreds of unused VIs are loaded into memory and the application is not responsive. To reduce the load time, I concluded that LVOOP classes cannot be used within the SubPanel VI's.

I am in the process of completing a "re-entrant Daemon w/LVOOP" and "Template UI w/no LVOOP" architecture to separate the UI from LVOOP. However, I wanted to poll the community to see if anyone else has run into this issue with large application design.

Any advice or suggestions to improve the performance of this framework would be greatly appreciated!

Link to comment

Since LVOOP requires the entire class to be loaded into memory and this is a large project, hundreds and hundreds of unused VIs are loaded into memory and the application is not responsive. To reduce the load time, I concluded that LVOOP classes cannot be used within the SubPanel VI's.

Why are hundreds and hundreds of unused VIs being loaded? Does your class have that many member vis? If so, I would guess the problem isn't with LVOOP or subpanels, but with your class design. It likely needs to be refactored and broken out into many individual classes rather than one gigantic class.

Link to comment

Why are hundreds and hundreds of unused VIs being loaded? Does your class have that many member vis? If so, I would guess the problem isn't with LVOOP or SubPanels, but with your class design. It likely needs to be refactored and broken out into many individual classes rather than one gigantic class.

When I say there are hundreds of "unused" VIs being loaded, the majority are "Accessor VIs".

All "Signals" inherit from a Signal base class. There are 12 classes that inherit directly from the Signal Class (Digital Input, Analog Input, Digital Output, Serial, CAN, ...) and another 10 which inherit from these (Relay Digital Output, Transistor Digital Output). It takes the same amount of time to launch a new VI from template that it does to load the class upon opening LabVIEW. Opening the Signal.lvclass takes about 45-60 seconds from which there are 419 files associated with the "Signal" class. Most of these are "Accessor" VIs that were created to provide access to class data through property nodes. That is an average of about 18 VI's per class which does not seem unreasonable.

The Signal objects have a Get/Set ValNumeric and Get/Set ValString for access to the hardware. What would be the problem with this class design?

Link to comment

What would be the problem with this class design?

On the surface, nothing. Had there been a single class with 419 member vis, *that* would have been a problem. (Your original post wasn't clear on that point.)

Opening the Signal.lvclass takes about 45-60 seconds from which there are 419 files associated with the "Signal" class.

This part isn't making sense to me. Loading a parent class doesn't trigger automatic loading for the child classes unless:

  • The parent class has source code dependencies on the child classes or,
  • The parent and child classes are contained in the same lvlib.

Loading a child class does require loading the parent class, but parents don't require children to be in memory. Why are all the children being loaded when the opening the Signal class? That's the first place I would look to improve performance. Furthermore, loading a child class doesn't automatically load sibling classes. Loading the Digital Input class will also load the Signal class, but it won't load the Analog Input class.

Also, if your "templates" are in fact vit files, you might consider changing them to reentrant vis. Jarrod comments on the benefits of reentrancy over templates on this thread.

Other than that, I think you're on the right track by separating the UI from the functional code.

Link to comment
Since LVOOP requires the entire class to be loaded into memory and this is a large project, hundreds and hundreds of unused VIs are loaded into memory and the application is not responsive.

I'm not going to comment on any design aspects. I assume you're design is good and this is what it takes. So let's focus on load time.

1) Put all your VIs and classes into a .llb file. The .llb optimizes the contents of the file and significantly improves load speed when a block of VIs must load as one.

2) Try loading in a runtime engine and compare to the load time in a development environment. Are they significantly different (there will be some difference, but it should be a second or two, at most) If so, you're probably doing something in the dev environment that is causing the block diagrams and panels of all those VIs to load needlessly. There are various things that can cause this -- scripting tools running in the background, VIs that are saved broken and become good after they load because something else loaded their missing subVIs, etc.

3) If you have LV 2010, you can build packed libraries. DO NOT UNDERTAKE THIS WITHOUT READING DOCUMENTATION. The packed libraries can give you BLAZING FAST LOAD SPEEDS. But they are a power that comes with consequences. Notably, the classes in the packed library have a different qualified name than classes not in the packed library, so any caller VIs need to be written in terms of one or the other. The packed libraries are designed to be "I build this separate component, and then I go develop something that uses that built component", not "I build this monolith of source code and then I turn it into two different components." There are tools to make the latter approach work, but the former approach is what I'd recommend.

Link to comment

Loading a parent class doesn't trigger automatic loading for the child classes unless:

  • The parent class has source code dependencies on the child classes or,
  • The parent and child classes are contained in the same lvlib.
Why are all the children being loaded when the opening the Signal class? That's the first place I would look to improve performance.
Just to make sure I understand this correctly, in which context is this true?
  1. When a .lvclass is opened in the IDE
  2. When a member of a .lvclass is opened

My current understanding is that #1 will trigger all VIs and child classes and your statement only applies to #2.

Also, if your "templates" are in fact vit files, you might consider changing them to reentrant vis. Jarrod comments on the benefits of reentrancy over templates on this thread.

Templates are required to load the same VI into more than one subpanel on the same front panel. See "How Do I Load the Same VI into More than One Subpanel of the Same Front Panel?". Does a reentrant template load differently when launched through VI server? If so, I have not seen any difference in load time.

I'm not going to comment on any design aspects. I assume you're design is good and this is what it takes. So let's focus on load time.

1) Put all your VIs and classes into a .llb file. The .llb optimizes the contents of the file and significantly improves load speed when a block of VIs must load as one.

2) Try loading in a runtime engine and compare to the load time in a development environment. Are they significantly different (there will be some difference, but it should be a second or two, at most) If so, you're probably doing something in the dev environment that is causing the block diagrams and panels of all those VIs to load needlessly. There are various things that can cause this -- scripting tools running in the background, VIs that are saved broken and become good after they load because something else loaded their missing subVIs, etc.

3) If you have LV 2010, you can build packed libraries. DO NOT UNDERTAKE THIS WITHOUT READING DOCUMENTATION. The packed libraries can give you BLAZING FAST LOAD SPEEDS. But they are a power that comes with consequences. Notably, the classes in the packed library have a different qualified name than classes not in the packed library, so any caller VIs need to be written in terms of one or the other. The packed libraries are designed to be "I build this separate component, and then I go develop something that uses that built component", not "I build this monolith of source code and then I turn it into two different components." There are tools to make the latter approach work, but the former approach is what I'd recommend.

Thanks for the suggestions to improve load time. It seems that a combination of #2 and #3 may help.

Packed Project Libraries - If I understand #3 correctly, it seems appropriate to implement the low-level driver code for each signal type as a packed library that would be included in and called from the respective "Signal" class? For example, within the Digital Input Signal class, I would call functions from the PXI_Digital_Input.lvlib for communicating with the cards." My seperate PXI cards (packed libraries for each type) are used by the Signal Class Objects to read and write hardware channels

Thanks for all the advice!

Edited by brianafischer
Link to comment

Requiring to package in a new format in a later version just to get normal performance is insane. Apart from that 2010 is 3x slower at loading sub-panels than 2009. I would expect a whole application of thousands of VIs to take less than 45-60 seconds to load (unless you are loading across a network) and that's if they are not in memory to begin withunsure.gif

Do your accessors override the base class by any chance? I believe dynamic despatch (required for overrides) might mean that depending on how you are calling the methods, all classes may have to be loaded even if they are not actually used since it is not known at compile - time which child will be used to override the base class(dynamic dispatch is just a VI selector after all and I doubt it loads from disk when you first call a particular method). Perhaps AQ can shed more light on that.though. There is some interesting reading here under The Design of Class Methods.

It could also be something to do with the fact you are using VITs and creating a new instance which needs to be cloned (can't think of a good reason why though). I would try what Daklu was suggesting and convert the VITs to VI's (re-entrant or not) just as a test to see if it improves. In theory, if the application has loaded the classes at start-up, then they should already be in memory so there should be no reason to re-load from disk so the only overhead should be the VIT itself (which should be quick).

Edited by ShaunR
Link to comment

Just to make sure I understand this correctly, in which context is this true?

  1. When a .lvclass is opened in the IDE
  2. When a member of a .lvclass is opened

My current understanding is that #1 will trigger all VIs and child classes and your statement only applies to #2.

The two contexts you present don't quite make sense to me in this discussion. First, "loading" is not the same as "opening." Opening a fp or bd will cause the vi to be loaded into memory if it is not already present, but a vi can be loaded into memory without the fp or bd being opened. Loading, not opening, is the operation we're interested in. Second, I'm not sure what you mean by "opening" a .lvclass in the IDE. You can open the class .ctl in Labview or open the .lvclass file in a text editor, but you can't really open a .lvclass in Labview.

As I'm sure you're aware, loading a vi into memory also causes all (statically) dependent vis to be loaded into memory as well. That's partly why "VI Trees" were used for so long--to make sure all vis were in memory so changes were properly propogated through the system. Libraries (lvlib, lvclass, xctl, and a few others) added additional loading rules into the mix.

  • Loading a library member (*.vi, *.ctl, etc.) always causes the owning library file (*.lvlib, *.lvclass, etc) to be loaded into memory.
  • Loading a class file (*.lvclass) into memory causes all class members (*.vi, *.ctl, etc.) to be loaded into memory.
  • Loading a project library file (*.lvlib) does not cause all library members (*.vi, *.ctl, etc.) to be loaded into memory.
  • Loading a library file (*.lvlib, *.lvclass, etc.) also causes all sub library files (*.lvlib, *.lvclass, etc.) to be loaded as well.

In context #1 above (and assuming you are referring to opening the .ctl) loading the .ctl loads the .lvclass, which in turn loads all member vis. In context #2 above, when you open a class member vi, that triggers loading the .lvclass, which in turn triggers loading all the other member vis. Both actions cause all class members to load. Neither action inherently causes child classes to be loaded into memory. For that to happen one of the two situations I mentioned has to occur.

One side effect of the library loading rules above is that if you have many classes contained in a single lvlib, loading any single class member causes all classes and class members in the lvlib to be loaded into memory. Are you using project libraries (.lvlib) in your application?

Templates are required to load the same VI into more than one subpanel on the same front panel. See "How Do I Load the Same VI into More than One Subpanel of the Same Front Panel?". Does a reentrant template load differently when launched through VI server? If so, I have not seen any difference in load time.

Good link. I'm not really qualified to answer this question as I don't typically use VITs or sub panels in my applications.

I'm curious how much code you have on each sub panel's block diagram and what their memory cost is? Since opening a VIT creates an entirely new vi rather than just allocating separate data space, I'm guessing it would be beneficial to keep that vi as small and simple as possible. You could give each signal sub class a "Subpanel UI.vit" method whose sole responsibility is to interact with the user and put all the sub panel's functionality into other class methods marked as reentrant. That might minimize the hit you take when opening a new VIT.

I believe dynamic despatch (required for overrides) might mean that depending on how you are calling the methods, all classes may have to be loaded even if they are not actually used since it is not known at compile - time which child will be used to override the base class.

Nope, at least that's not my understanding. You don't have to know which child object will be used to override the base class at compile time.

"A dynamic dispatch subVI node on the diagram records a particular index number when it compiles. A node that represents an invocation of A.vi, for example, records index 0. A node for an invocation of B.vi would record index 1. At run time, whenever an object comes down the wire, the node will access the dynamic dispatch table of that object. It will retrieve the VI at the recorded index and invoke that VI. The node does not incur any overhead of name lookup or searching lists to figure out which subVI to call. The time complexity is O(1) no matter how deep the inheritance tree gets or how many dynamic dispatch VIs the classes define."

If child classes had to be known at compile time many plug-in frameworks would be impossible using LVOOP.

Link to comment

Nope, at least that's not my understanding. You don't have to know which child object will be used to override the base class at compile time.

<snip>

If child classes had to be known at compile time many plug-in frameworks would be impossible using LVOOP.

"Each object traveling on the wire carries a pointer to its class information (refer to the "What is the memory layout of a class?" section earlier in this document). That class information includes the "dynamic dispatch table," which is a table of VI references. Each class copies its parent’s table exactly. It then replaces the VI reference for any parent function with its own overriding VIs"

I'm thinking in the specific case of overrides. From what little I understand of dynamic dispatch in general cases. It seems no different from a polymorphic VI i.e it chooses the implementation at edit time).

But for overrides (which Is what I perceive to enable run-time polymorphism) The statement above seems to suggest that the class VIs are already in memory (For the table to have the VI reference it must, I'm assuming, have already loaded the VI's in the class with an equivalent of "VI Open reference" and resolved any dependencies). If it is, as I think you are suggesting, that it lazy loads from disk with a sort of "load on first call" then that would (whilst maybe convenient for plug-ins) have a dire performance impact in many cases. Maybe the "lazy-load" is the fall-back mechanism if other methods are unable to identify the call chain to populate the table (I think the project/class manager is doing a little bit more than we might think).

I suspect (but don't categorically know) that all the class VIs are actually loaded in the case of overrides and, unlike in the general case where it is known before-hand what VI's will be used;;all VIs that override are loaded just in case it may be used at run-time. I suspect also that you can't just load a part of a class (i.e just the VI's used in overriding) so that the entire class must be loaded. This view would seem to fit with what the OP is seeing hence my question about the use of overrides.

Edited by ShaunR
Link to comment

I'm thinking in the specific case of overrides. From what little I understand of dynamic dispatch in general cases. It seems no different from a polymorphic VI (i.e it chooses the implementation at edit time). But for overrides (which Is what I perceive to enable run-time polymorphism)...

I don't understand... dynamic dispatch and overrides essentially the same thing--two sides of the same coin. Can you explain further?

[After thinking about your comment for a while...]

Maybe this is what you're referring to as the polymorphic behavior? Suppose I have Parent:A.vi and Child:A.vi. On a new vi I drop a Parent control and Child:A.vi. When I wire them together Child:A.vi automatically turns into Parent:A.vi. (It also works with a Child control and Parent:A.vi.)

That isn't the same thing as a polymorphic vi. Even though the vi on the block diagram morphs into a different vi, it's still a dynamic dispatch call. I can send a child object through the parent control at run time and child's implementation will be called. I view that morphing behavior as a developer convenience to help code clarity more than anything. Execution-wise it doesn't change anything.

post-7603-0-45293800-1303485461_thumb.pn

If it is, as I think you are suggesting, that it lazy loads from disk with a sort of "load on first call" then that would (whilst maybe convenient for plug-ins) have a dire performance impact in many cases.

I'm not suggesting a "lazy-load" mechanism, as we don't have that level of visibility or control over Labview's memory operations. All we developers know is that Labview loads things into memory when it needs them and releases them when they are no longer needed.

I suspect also that you can't just load a part of a class (i.e just the VI's used in overriding) so that the entire class must be loaded.

This is correct. Loading a class is an all-or-nothing proposition.

I suspect (but don't categorically know) that all the class VIs are actually loaded in the case of overrides and, unlike in the general case where it is known before-hand what VI's will be used;;all VIs that override are loaded just in case it may be used at run-time.

If there is a reason for the child class to be loaded into memory, then yes, all of the class' members will be loaded into memory as well. However, loading a parent class isn't a reason to load child classes, even if the child classes have overriding methods.

Link to comment

If there is a reason for the child class to be loaded into memory, then yes, all of the class' members will be loaded into memory as well. However, loading a parent class isn't a reason to load child classes, even if the child classes have overriding methods.

Indeed. But to call a child it must be laded at some point either prior to the actual call (pre-loaded) or when the call is executed (lazy loaded). Like you said. We don't have visibility of the mechanics. But from the quoted piece of text, it seems that the purpose of the dynamic dispatch table is to maintain a VI reference list. and the object passed determines from which VI reference the method is executed. I am speculating (in response to the OPs observations) that to get those references it must load the VIs. From that, I am suggesting, as it doesn't know which classes "will" be called until it actually happens. The algorithm pre-loads all classes than "can" be called to populate the table.

The alternative (I cannot see any others, but there may be one) is you place an object on the wire that indexes past the end of the list triggering some loading voodoo when the call takes place. As you say, you cannot load a part of a class, so the latter could be a long wait as it loads every method for the new class (and dependencies) - right in the middle of your code executing.

The former would explain the OPs obserevation. The latter would be a very good reason not to use DD-especially on real-time systems.

Link to comment

Indeed. But to call a child it must be laded at some point either prior to the actual call (pre-loaded) or when the call is executed (lazy loaded).

Yep, that is true. But it doesn't imply the child class has to be loaded at compile time. My comments have been in resonse to this...

"I believe dynamic despatch (required for overrides) might mean that depending on how you are calling the methods, all classes may have to be loaded even if they are not actually used since it is not known at compile - time which child will be used..."

A child class is loaded only if the child class is used (or if it is a member of a project library that has been loaded.) In this context, I'm interpreting "used" to imply there is a direct, static dependency on the child class. IOW, a member of the child class exists on the fp/bd of a vi that is in memory.

But from the quoted piece of text, it seems that the purpose of the dynamic dispatch table is to maintain a VI reference list. and the object passed determines from which VI reference the method is executed. I am speculating (in response to the OPs observations) that to get those references it must load the VIs.

Probably correct... or close enough for our purposes.

From that, I am suggesting, as it doesn't know which classes "will" be called until it actually happens. The algorithm pre-loads all classes than "can" be called to populate the table.

This, if I'm understanding what you're saying, is incorrect. The compiler doesn't need to know any of the child classes that can be called because there isn't a universal dynamic dispatch table that needs to be populated; each class has its own dd table that is created/modified at edit time and loaded into memory when that class is loaded.

Suppose I have a Parent class and a Child class with a single overridden method A. Then I create a naked vi like this:

post-7603-0-83072700-1303498812_thumb.pn

I can load and unload this vi all day long in the IDE without causing the child class to be loaded. I can add and remove data or methods without loading the child class. I can build it into an executable and still the child class isn't loaded. Through all this Parent:A is still a dynamic dispatch method and will call the child method if it sees a child object on the wire at run time. How?

For starters, before a child object can be instantiated, the class must be loaded. It doesn't matter when this loading takes place--it may be when the application starts or it may be right before the object is needed. (Even though we don't have have lazy loading directly available to us, we do have some control over when things are loaded.) Loading the child class loads the child's dd table, along with all the child's members.

Then dynamic dispatching injects a small bit of functionality (I'll call it the "dynamic dispatcher") between the connector pane terminal and the block diagram. At run time, after an object arrives at Parent:A's class input terminal, but before Parent:A is invoked, the dynamic dispatcher says to the object, "This is dynamic dispatch method #1. Give me a reference to the vi you have listed in your dd table at index 1." The object returns the reference and the dynamic dispatcher invokes that vi. Parent objects will return a reference to Parent:A. Child objects will return a reference to Child:A.

At compile time the Parent class doesn't need to know about Child classes because the children create their own dd tables when they are compiled. (Note that loading a child class does trigger loading all ancestor classes.)

Link to comment

For starters, before a child object can be instantiated, the class must be loaded. It doesn't matter when this loading takes place--it may be when the application starts or it may be right before the object is needed. (Even though we don't have have lazy loading directly available to us, we do have some control over when things are loaded.) Loading the child class loads the child's dd table, along with all the child's members.

Red: I think this is where we are at odds. I think it matters very, very much.

The loading of the VIs in a class is orders of magnitude slower than instantiating an object. One is a disk operation and the other is just allocating memory. The big caveat with classes is that a class doesn't just load what it needs (as it does in classic LabVIEW). It has to load everything in the class - needed or not. Pre-loading these method VIs is the only practical solution to trade the 1 or 2 seconds (say) of disk activity for a few microseconds/milliseconds of execution and repeatability/consistency of calling those methods.

If, however, it is loaded just before it is required. If may be that that 1 or 2 seconds for LabVIEW to load the class from disk is in the middle of a 100 ms timeout, or a timed measurement. Then it is a real problem.

Blue:Exactly.

But the way the DD table works is that it is a list of VI references in the order of of the hierarchy (parent at the head, child appended to end). If we want to prevent the second scenario as described above from happening. then it will have to load the parent AND the children from disk before the call. If it is an overridden parent. then it will have to load the parent, child and all the siblings.

Then dynamic dispatching injects a small bit of functionality (I'll call it the "dynamic dispatcher") between the connector pane terminal and the block diagram. At run time, after an object arrives at Parent:A's class input terminal, but before Parent:A is invoked, the dynamic dispatcher says to the object, "This is dynamic dispatch method #1. Give me a reference to the vi you have listed in your dd table at index 1." The object returns the reference and the dynamic dispatcher invokes that vi. Parent objects will return a reference to Parent:A. Child objects will return a reference to Child:A.

Yes. Absolutely. It returns with a reference. To have that reference it must have loaded that VI. As it cannot load a single VI from a class. It must have loaded the entire class If it didn't know before-hand what class it would need, it must have loaded all of them. Unless it lazy loaded the class when the call was made.

This is one way that I think about what the "mechanics" might be (in the design environment in this case). There are a few ways that the blue box area could operate (they could be sequential, depth first et al.), but it's basically how I see the partitioning between loading. and calling in reference to the document.

From this I think you can see that I believe (I'm only guessing after all) that all classes are loaded before any calls - loaded, not instantiated. I think instantiation is just semantics to keep POOPers happy since I could say that by placing a common or garden VI on a diagram I am loading and at the same time instantiating that VI. In In this case it's just a parent and child. I think that if you override, then siblings would also be loaded. In the image, your "dynamic dispatcher" is the index array element primitive.

Edited by ShaunR
Link to comment

Red: I think this is where we are at odds. I think it matters very, very much.

I meant that it doesn't matter to Labview. I agree that it may matter to the application developer. In those cases it is important for the developer to understand the conditions that cause the class to be loaded into memory and make sure loading doesn't interfere with execution.

But the way the DD table works is that it is a list of VI references in the order of of the hierarchy (parent at the head, child appended to end)... It must have loaded the entire class If it didn't know before-hand what class it would need, it must have loaded all of them. Unless it lazy loaded the class when the call was made.

No, your mechanics model is wrong. It has the dynamic dispatch table as a separate entity from the objects when in fact each object carries it's own dynamic dispatch table.* The parent class' dynamic dispatch table only contains references to parent class methods. The child class' dynamic dispatch table can contain references to parent class methods (if it didn't override the method) or its own methods (if it did override the parent method or added new dd methods the parent doesn't have.)

[*Dynamic dispatch tables are part of the class definition, not the object data, so I doubt each object "carries" around a duplicate copy. I assume each object simply points to the dd table defined for it's class.]

Loading a parent class doesn't require loading child classes because the parent's dd table doesn't have any references to child methods. How does the parent class know which child method to call? It doesn't. At runtime if there is a parent object on the wire a parent method will be called. A child method can be called only if there is a child object on the wire. If there is a child object on the wire the child class must have already been loaded, which also loads the child's dd table (since it is part of the class definition.)

Clearer?

[Edit]

There are a few ways that the blue box area could operate (they could be sequential, depth first et al.), but it's basically how I see the partitioning between loading.

The blue box area isn't quite correct either. Whether or not the class is loaded at the blue box depends on what the blue box is. If it is a vi or lvlib that is in memory, then the classes are loaded. If it is a project editor window then the classes have not necessarily been loaded, but may have been loaded depending on what else you've done during that editing session.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.