Mike Le Posted February 4, 2014 Report Share Posted February 4, 2014 (edited) Hi all, We've been running into serious problems with our project load and build times lately. One suspected culprit is how complex our class inheritance has become. Over time, we've created a lot of abstract classes to organize useful methods. For example, we have our own Actor Base Class, which contains some methods we've found useful (debugging, error handling, etc). In addition, we have base classes for certain broad categories of functionality, like Hardware Communication, UI, Calculation, etc. The UI tree in particular goes very deep. For example: Our Actor Base Class --> UI Actor --> Subpanel Actor --> Subpanel Abstract Type for Specific App --> Subpanel Concrete Class. We're hesitant to restructure, because the way methods are separated into different layers now is extremely useful. But we've also heard that simpler inheritance improves loading and build performance. It would be easier to prune levels off the non-UI trees, but those are (1) already less complex than UI and (2) those classes aren't used that often. Would that help or would that be a wasted effort? Basically, how are other people balancing load times with the utility of inheritance? Pitfalls? Other organizational tips? Thanks. EDIT: More extensive forum searching turns up this old thread, which seems related. I'll look into the solutions suggested there for improving load times, too. The thread suggests packed libraries. If we throw some of our higher-level parent classes into a packed library, then would that improve loading times at all? Is it even possible to inherit from a class inside a packed library? In other words, are using packed libraries a good way to mitigate lots of inheritance levels? We ideally wouldn't be messing with the parent classes very often. Edited February 4, 2014 by Mike Le Quote Link to comment
Mike Le Posted February 4, 2014 Author Report Share Posted February 4, 2014 My coworker Tom found this presentation describing building a parent class into a packed library and then having plugins inherit from it. This seems exactly like what we were thinking about doing. One thing that the presentation lacks (perhaps because it's considered obvious?) is the advantages of having a parent in a packed library. For example, does it partially resolve the extended project open and build times? Does it just reduce the time to load the parent class? It seems like many people are interested in packed libraries for clean, professional-seeming deployments. While that's all well and good, I'm more interested in if it can do anything to mitigate our pain as developers. Quote Link to comment
ShaunR Posted February 4, 2014 Report Share Posted February 4, 2014 (edited) It seems like many people are interested in packed libraries for clean, professional-seeming deployments. While that's all well and good, I'm more interested in if it can do anything to mitigate our pain as developers. They have identical issues to removing diagrams from VIs. i.e. not cross platform or cross CPU arch.For most developers that create tool-kits, utilities or reusable components that are distributed to other devs; they are not a good choice. If you only have one product and only ever use one OS of a single bitness and that will always be the case then that's fine. - welcome to Labviews version of DLL hell I personally avoid them like the plague-little upside, huge downside. A LLB is superior IMO. However. In your case. I can't help but feel the only reason you are considering them is through desperation - to cure the symptom of a much deeper problem. Edited February 4, 2014 by ShaunR 1 Quote Link to comment
Mike Le Posted February 4, 2014 Author Report Share Posted February 4, 2014 However. In your case. I can't help but feel the only reason you are considering them is through desperation - to cure the symptom of a much deeper problem. Yes, there's that. I don't suppose you have a rope to toss down to us trudging through the deep end? Quote Link to comment
ShaunR Posted February 4, 2014 Report Share Posted February 4, 2014 (edited) Yes, there's that. I don't suppose you have a rope to toss down to us trudging through the deep end? I think the only person that could help is probably AQ. It's been known for some time that LVOOP takes a long time to compile (I have commented before to those that think hours to compile is acceptable). However. I actively avoid LVOOP so my experience is limited for applications. Some might say I have softened a little since I do on occasion use it for trivial APIs to save connectors. The kind of advice I would proffer would involve converting to classical LabVIEW. I expect that isn't an option for you, though, if your whole architecture is OO.. There was some discussion a while back about mutation history getting very large and bogging down load times. Daklu was moaning about it; I believe. You might try and find it on this forum (I couldn't after a cursory look) and try experimenting with deleting the mutation history (see the conclusion and make sure you have backed everything up) for a class or two and see if things improve. But that's just a wild stab in the dark. Edited February 4, 2014 by ShaunR Quote Link to comment
PaulL Posted February 4, 2014 Report Share Posted February 4, 2014 Mike, From what you say I would venture that the problem is that there are very many source code interdependencies, which will result in lengthier load times. If that is true, packed project libraries (which I generally don't recommend anyway) will not help. The appropriate solution is to use interfaces to minimize source code interdependencies. (I explain how to create an interface--or something very much like one--in several of my papers linked in LAVA threads. Basically you will need to add a layer above your existing abstract classes, and this new layer will have abstract methods as well. There are other steps to take to minimize interdependencies, but this is the main one.) I do not buy the argument that greater hierarchy depth alone dramatically increases load time. For instance, I have a class hierarchy 6 layers deep in my current project, which is not at all atypical in this part of my applications, and load times are quite normal. Paul 1 Quote Link to comment
Mike Le Posted February 5, 2014 Author Report Share Posted February 5, 2014 From what you say I would venture that the problem is that there are very many source code interdependencies, which will result in lengthier load times. This could very well be the root cause. Is there a good way to determine how interdependent the code is? Also, could you point to one or two of your papers that might be applicable here? One of the very big problems with our code is that it's been slowly developed over the past 3-4 years. Naturally there's been a lot of "extension" (what might be less politely called "feature creep"). For the first 2 years, our code was exclusively task-oriented, so we had lots of state machines that just spiraled out of control and now talk to each other in whatever way was convenient to tack on at the time. All our new code is OO, and I think we've done a pretty good job of encapsulating functionality with our new classes. But we haven't had the time to "stop the presses" and re-factor all of our projects into OO. Quote Link to comment
PaulL Posted February 5, 2014 Report Share Posted February 5, 2014 I recommend you check out the appendices in the paper linked in this thread: http://lavag.org/topic/14213-strategy-pattern-example/?hl=%2Bstrategy+%2Bpattern. One way to see the interdependencies is to create an empty project and add, say, a class to it, and see what comes along with it. Look at the tag-alongs and think about whether the linking is logically necessary or not. Paul 2 Quote Link to comment
K-node Posted February 5, 2014 Report Share Posted February 5, 2014 I periodically do what Paul suggests; adding what I think might be a class with few dependencies. Before doing that set the 'Arrange By' of the Project's My Computer to be Custom. The Dependencies should already be 'Same as Parent'. You have a better chance at figuring out where that odd dependency came from when you 'Find callers' on a class, class data ctl or class method. Quote Link to comment
drjdpowell Posted February 6, 2014 Report Share Posted February 6, 2014 I find I have to watch out for optional VIs that extend a class’s functionality by using another class. It feels natural to include the optional VIs in the class; but, a class aways loads all it’s contained VIs. So this means that the other class and its dependancies always load when the first does, even if the optional VI isn’t used. If the unneeded-but-loaded classes also have optional VIs that use still other classes, then those classes load. It only takes a few “linker” optional VIs to cause huge numbers of unused classes and VIs to load. The key is to keep these VIs outside the class they are naturally a part of (I organize them in LVLIBs, often with the same name as the parent class). I once created a new project and added my top-level “Message” class, and this loaded twenty to thirty unneeded classes, all traced to two half-forgotten “linker” VIs. From a name-spacing standpoint this is rather undesirable, but until LabVIEW fixes the issue of a class always loading all it’s VIs it’s the only solution. — James 1 Quote Link to comment
PaulL Posted February 6, 2014 Report Share Posted February 6, 2014 OK, the other advice I have for someone moving forward designing systems using objects (not because objects are bad but because there are object-oriented design tools that make this straightforward) is to design with the goal of eliminating inappropriate code dependencies in mind. (This is part of the high cohesion and loose coupling concept, which encapsulation helps accomplish.) Skillful application of the UML, with some deep thinking in the process, makes this a lot easier! Paul Quote Link to comment
Mike Le Posted February 8, 2014 Author Report Share Posted February 8, 2014 Thanks for all the great advice, guys. My coworker ran Paul's suggested experiment of dropping a class into a new project. The number of dependencies was surprisingly large, but at least now we have a baseline from which to work on better encapsulation. One question: if a single member of an LVLIB is loaded into memory, then do all the members of the LVLIB also get loaded? We have a library with some of our commonly used "primitives." We're afraid that any time we use one of those primitives, we're loading the entire library into memory. Is this the case? If so, should we work on making our LVLIBs smaller and more compact? Quote Link to comment
drjdpowell Posted February 8, 2014 Report Share Posted February 8, 2014 An LVLIB loads all its contained libraries and classes, but not its VIs. So it is OK to have lots of VIs in a LVLIB, even if not all will be used. It is only LVCLASS that loads all contained VIs. Note that if you have a class in a LVLIB, loading the LVLIB will load the class and thus all the class’s VIs. I generally do not put classes in libraries for thus reason. NI really should work at changing this. I can see why it might be necessary to load all dynamic-dispatch VIs, but not static ones. 2 Quote Link to comment
Mike Le Posted February 8, 2014 Author Report Share Posted February 8, 2014 (edited) An LVLIB loads all its contained libraries and classes, but not its VIs. So it is OK to have lots of VIs in a LVLIB, even if not all will be used. It is only LVCLASS that loads all contained VIs.Note that if you have a class in a LVLIB, loading the LVLIB will load the class and thus all the class’s VIs. I generally do not put classes in libraries for thus reason. NI really should work at changing this. I can see why it might be necessary to load all dynamic-dispatch VIs, but not static ones. Thanks, James, super informative and helpful. So given this, then I think I'm a bit confused about this part: It only takes a few “linker” optional VIs to cause huge numbers of unused classes and VIs to load. The key is to keep these VIs outside the class they are naturally a part of (I organize them in LVLIBs, often with the same name as the parent class). Given that loading a single member VI loads an LVLIB, which subsequently loads any lvclasses, then isn't including the optional VIs in the same library problematic? Sorry if I'm misunderstanding something fundamental, but I really want to understand this as we untangle our dependencies. Thanks again! Edited February 8, 2014 by Mike Le Quote Link to comment
drjdpowell Posted February 8, 2014 Report Share Posted February 8, 2014 Given that loading a single member VI loads an LVLIB, which subsequently loads any lvclasses, then isn't including the optional VIs in the same library problematic? Very problematic, but many VIs are naturally thought-of as methods of the class. What if you later need to override the Optional VI in a child class, for example? You’ll have to change the library membership, and thus namespacing, with the headaches that entails. And if nothing else, it destroys the usefulness of using libraries to organize related code, if you have to place closely related VIs in different libraries. It’s just ugly! Quote Link to comment
Mike Le Posted February 9, 2014 Author Report Share Posted February 9, 2014 Sorry, I think I'm still missing something here. What's the goal of placing the "optional VIs" OUTSIDE the lvclass, but inside the same LVLIB as the class? I thought this was to avoid being forced to load the class when you want to use the optional VI. But if it's in the same LVLIB as the class, then won't both load anyway? In that case, is the advantage purely from an "organizational" standpoint? I guess I'm asking: why not just stick the optional VIs into the lvclass given that they're both going to load together either way? Quote Link to comment
drjdpowell Posted February 9, 2014 Report Share Posted February 9, 2014 What's the goal of placing the "optional VIs" OUTSIDE the lvclass, but inside the same LVLIB as the class? As a rule, I don’t put classes in libraries at all, and the optional VI are in separate “support” libraries that only contain VIs. I thought this was to avoid being forced to load the class when you want to use the optional VI. But if it's in the same LVLIB as the class, then won't both load anyway? Other way around; the Optional VI uses the class (it’s really a static method of the class in form), so always loads the class, but loading the class doesn’t load the Optional VI. 1 Quote Link to comment
Mike Le Posted February 9, 2014 Author Report Share Posted February 9, 2014 Ahhh, much clearer now, thank you. I use Actor Framework extensively, so all my Actors have their own library and are organized with their associated messages. I think that's why I assumed the classes were in libraries as well. Thanks for all the lengthy explanations; sorry it took so long for me to grasp. Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.