Jump to content

Aristos Queue

Members
  • Posts

    3,183
  • Joined

  • Last visited

  • Days Won

    202

Posts posted by Aristos Queue

  1. I however noticed another annoying thing. It takes 30 seconds after I press run button for my main VI to start actually running. According to profiler it actually runs for 0.01 seconds, so the rest of the time LabVIEW is doing some internal tasks... I verified that this is actually the case by placing some probes on the diagram to follow the execution. The first class constant was initialized after thirty seconds.

    Known issue... the performance hit is due to excessive recompiling. Already being dealt with.

  2. You see, there's your mistake. This is LabVIEW, so you should have been banging your head on your mouse. It would probably have hurt less and could even potentially create some useful code. :laugh:

    Hm... perhaps LV should encourage computer vendors to make mice with a pressure gauge. If we notice both mouse buttons hit simultaneously with extreme force repeatedly, we'll automatically launch a web page that lists all the major help forums (ni.com, info-LV, LAVA, etc?). :P

  3. If you want one that really makes you want to claw your eyes out, take a look at the output terminal of the Type Cast node...

    *(type *)&x

    Changing these names once they're established is non trivial. Unless they're really factually wrong, such changes tends to be lower priority than all the other stuff that needs working on in LV.

  4. Everything else i can pretty much nut out but have no clue about this "Quotient and Remainder" function.

    Think back to elementary school when you first did division, before you knew about decimals.

    What is 7 divided by 2 ?

    Answer: 3 remainder 1.

    The 3 is the quotient. 1 is the remainder.

    This is also known as modular division or integer division.

  5. One other little note: when you have a queue of 1D arrays of <whatever>, each enqueue operation can pass in a different size array, i.e., an array of 49 elements, followed by an array of 3117 elements, an array of 3 elements, etc. Of course, this may not necessarily be the friendliest thing to do to your downstream data consumer...

    -Kevin P.

    Curious that you should bring up queues of arrays today....

    For reasons of my own, I was reviewing the behind-the-scenes code of the queue primitives today. One of the biggest use cases I have for them in my own programming is tree traversal. For example, control reference traversal: enqueue all the controls on the front panel, then in a loop, dequeue them one by one to do something to them. If the one you dequeue is a tab control or a cluster, enqueue all the sub controls. Continue looping until the queue is empty.

    Now, what I usually do is create a queue of my base type (for example, control refnum). When I have an array of elements, I drop a for loop and enqueue each element one by one. That way they're all available for dequeue.

    I got to thinking -- there are some times when I'm enqueuing each individual element and then at the dequeue I'm building an array of elements back up (maybe a filtered list of the elements, for example). In these cases, it seems like it might be beneficial to have two queues, one that handles single elements and one that handles arrays of elements so that I never bother tearing down the array structure if I'm just going to build it again. Of course, this is for traverals where the order of traversal doesn't matter, since you would then dequeue from the lone element queue until it was empty then dequeue from the array queue. Since the queues try to simply take ownership of the wire's data and not make a copy of the data (unless the enqueue wire is forked to some other node in which case it has to make a copy), it might make sense in some cases to let the enqueue take ownership of the entire array of elements.

    I don't have any specific examples at this point. And I don't have any evidence that this would ever be advantageous. It's just one of those passing hunches to think about....

  6. I am stopping all this stuff shortly, been programming for 40 years almost, time to go fishing.

    Adapted from The Tempest, by William Shakespeare

    Ye LVs (elves) of nodes, wires, shift registers, and tunnels;

    And ye that on the wires with custom probe

    Do chase the flowing data, and do show it

    err it ebs again; you VIs that

    By moonshine do the carry data from diagram to panel,

    Whereof we view its bytes; and you whose pastime

    Is to make indicators light, that rejoice

    To see the waveform charted; you, by whose aid,

  7. LabVIEW can handle cases 1 and 4 (from link above), and sort of handles the third case by timing out with a time of zero milliseconds. I'm concerned :( that my implimentation of Case 2 with "Enqueue -> Timeout True? -> Dequeue Element -> Enqueue Element" will add to the CPU load when working with multiple TCP data streams.

    I looked long and hard at your picture. A few thoughts...

    a) Are you worried about the unnecessary loss of data? There's no "critical section" protecting the "enqueue, dequeue, enqueue" sequence. Suppose the producer VI tries to enqueue and fails. Ok, so it goes to the dequeue to make room. In the time it takes to do this, the consumer VI has dequeued an element. There's no need for you to do the dequeue, but you don't know that. I don't think this matters -- most lossy streams, such as video conference communication packets, don't really care what packets get dropped. If this matters, a semaphore aquire/release around the "enqueue, dequeue, enqueue" and the same semaphore acquire/release around the dequeue in the consumer loop would fix the problem.

    b) I think I can suggest a better performing way of doing the "enqueue, dequeue, enqueue." Your current implementation will fork the data being added to the queue and will hurt performance for large data elements. Try this:

    post-5877-1158273876.png?width=400

    The Queue Status primitive generates no code for any terminal that is unwired. So if you do not wire the "Elements Out" it will not duplicate the contents of the queue, nor will it take the time to evaluate any of the unwired terminals. Fetching the current element count is very fast, and this avoids ever forking your data wire. Forking the wire is a big deal since if the wire is forked it prevents the Enqueue from taking advantage of one of its biggest speed optimizations and it guarantees a copy of the data will be made at the fork.

    (PS: The 0 that I've wired to the timing input of the dequeue inside the case structure is important... you might detect that the queue is full, so you go to dequeue an element... in the time between when you detect the queue is full and the dequeue, the consumer loop might speed ahead, dequeue all the remaining elements and leave the queue empty. If the timeout terminal of the dequeue is unwired, the dequeue would hang indefinitely waiting for someone to enqueue data. These are the sorts of gotchas that multi threading opens up for you. :D )

    c) I know you said "and don't tell me about variants." Although probably not the solution at the moment for whatever it is that you're working on, as time goes on I would expect those utility VIs that you discuss to be writable with LabVIEW classes, where there is no extra data allocation when you upcast. Over time I believe that users will find a lot of utility in rewriting functionality, particularly with generic data communications systems like the queues or LV2 globals, using the LabVIEW classes for maximum code reuse and minimum "genericization" overhead. Just a thought... I'm downplaying the possibilities here since I've lately been accused of suggesting LV classes as *the* silver bullet for all of LV's problems. I want to keep expectations realistic, but I do think there's benefit in this arena.

  8. I still verify that aren't the VIs in memory when I open a project containing them? So I still explicitly need to open a reference for each VI, method VI, control, polymorphic VI, library, class and VI template in my project and after that do the ctrl+shift+click trick?

    That's correct. Opening a project simply opens a file listing. It does not load all the VIs into memory (the project is designed to be a manager for very large projects where you might not want all the VIs in memory simultaneously). The same is true for a library -- .lvlib files do not load all their VIs into memory.

    .xctrl and .lvclass files do load all their member VIs into memory.

    VIs load their entire subVI hierarchy into memory, and polymorphic VIs load all of their instances into memory.

    Summary:

    File extensions that do not load all of their referenced files into memory:

    .lvproj

    .lvlib

    File extensions that do load all referenced files into memory:

    .vi

    .ctl

    .vit

    .ctt

    .xctrl

    .lvclass

  9. I ended up mass compiling since I couldn't start LabVIEW anymore. One of the class probes, Icon Probe of my Class Iconizer project also published here at LAVA, located under user.lib started... <snip>

    Mass compile won't fix this bug -- the whole problem is that LV doesn't think there's any reason to recompile. Mass compile does a load, check for whether anything has changed, and only do recompile if it needs to.

    To fix the Icon Probe bug, find this token in your .ini file:

    ProbeDefaultCache

    Delete the problem probe from the list of paths.

    If you don't have one or two top level VIs that will load everything, you can write a quick VI Server routine to load all the VIs in a directory. Once everyone is in memory, use the ctrl+shift+click on run arrow that I mentioned before.

    None of this, by the way, is correct expected behavior.

    *sigh*

  10. Edit 2: I created a dummy project and was unable to repeat the problem in this project. So it seems the error is somehow related to my real work project. I cannot post my project here since it is non-public. I have got LabVIEW 8.2 to crash so many times that I may very well be that the project is somehow corrupted, even though it seems to be functional. Any ideas how to fix a corrupted project. I cannot go back to non-corrupted backup since I have no idea when the corruption has occurred. I also have no wish to code everything again. I cannot use "Save as" for my libraries since this is the easiest way to get everything meshed up and to get LabVIEW to crash. This is a problem that never occurs in text based programming languages.... :question:

    Jimi: Try loading your project into memory, get all your VIs into memory (by opening the FP of all your top-level VIs) and then do ctrl+shift+click on the run arrow. This will force a recompile of all VIs in memory. I'm tracking a bug where you make an edit to the class' private data control and save the change. We recompile all the VIs that bundle/unbundle the class. Then you exit LV without saving all the VIs. When you next load, we don't seem to be noticing that the types are out of date and so we aren't recompiling the bundle/unbundle VIs automatically. So they have stale memory addresses and may crash when run. Forcing a recompile of all the VIs in memory catches these guys and brings them up-to-date.

    This was reported to R&D (# 40667H1W) for further investigation.

  11. By this, do you mean that another VI can not obtain a refnum for an unnamed queue created elsewhere (i.e., with Obtain Queue)? I have no problem creating an unnamed queue in a top level VI and passing that queue's refnum to a subVI for use there.

    Sorry for the confusion... yes, I mean that no other VI can obtain a reference. You can pass the reference to a subVI, or send it to another VI through some other communications mechanism, but basically only those VIs to which you publish that refnum can use that refnum.

  12. Its too bad the children can't just inherit the DefaultValue.vi method from Device.lvclass, but if you do this the instaces created don't have the information about which child they are an instance of (sorry for the bad grammar). If I put a "DefaultValue.vi" type method in every child, then tehy all have to have unique names which might lead to trouble down the road when we merge in code developed by other groups.

    The VIs in each child do not have to have a unique name as long as there is no VI of that name in the parent class. For example:

    Class Parent has two children, Kid and Junior.

    Kid can have a static method DoStuff.vi.

    Junior can also have a static method DoStuff.vi.

    The above is true as long as Parent does *not* have a method named DoStuff.vi. If Parent does have a method named DoStuff, then it must either be dynamic (and Kid and Junior become overrides) or, if it is static, then Kid and Junior cannot have a VI with that name.

  13. I obtain almost all my queues by name. I also like to be able to keep track of where queues are being used and monitor them, so I often use a couple of wrapper VIs to facilitate housekeeping. If a VI wants a queue, it calls the wrapper, which returns the queue, but also updates tables of queue name and who (which VIs) are using each. It lets me do a simple monitor that shows all the named queues in an application and loading. If you put things in debug and single-step modes and watch the queue list it sometimes helps in debugging a big app that uses a lot of queues.

    YMMV

    An unanmed queue cannot be accessed by any other VI. It is the absolute best way to ensure no name collisions occur -- without a name, there cannot be a collision. All of the examples that ship with LV try to avoid naming queues unless there's a driving need (multiple VIs obtaining references to the same queue). That way we don't accidentally have an example that interferes with another example or, worse yet, interferes with other VIs the user is running.

    Giving them names does help debugging, as you point out, but I'd wrap that up so that I could turn it off in production code. A named queue is useful for client/server architectures and multiple-dynamic-VI instantiations, where you want lots of VIs to talk to the same queue, but you don't know which VI will be the last to shut down, so you want each VI to have its own reference to the queue.

  14. I'm going to answer your question on data inheritance first... I'll get to your "alternate ways to do dynamic instantiation" question in a moment.

    Also, I'm still not clear on the inheritance of the data (I'm sorry, sometimes I just don't get it). Going back to my original post and the idea that an instance of a child class inherits the data clusters of its ancestors. This is implied in several places in what I have read. But is it really the case that you can't access this data in a child method but rather you need to add a method the ancestors to expose this data to the child? Perhaps I am cofused by classes only having private data in LabVOOP.

    Every instance of the child has the parent's data. But although the child class carries this data around with itself, no function owned by the child is allowed to read or modify the data.

    You said you speak C++... so let me write some equivalent code.

    post-5877-1157742123.png?width=400

    Ok... in the above code, is there any way for any function to ever change the value of x ? The answer is no. Every instance of Child will have two integers inside it -- x and y -- but there is absolutely no function that has the authority to change the value of one of those integers. If Child is going to have access to x, then Parent needs to supply get and set functions... like the ones shown in the red box:

    post-5877-1157742198.png?width=400

    This same idea of get and set methods is required in LV.

    Make sense?

    Are there any other possibilities of dynamically creating instances of a class?

    I just realized there's a possible terminology collision here. I've been assuming one particular meaning, but I'm not sure that meaning is what you intend, given the other the details in your latest post.

    You use the phrase "dynamically creating instances of a class."

    Do you mean

    A) "Assuming a class definition is already in memory, are there any other possibilities for creating instances (objects) of that class?"

    or do you mean

    B) "Are there any other possibilities for dynamically loading a class definition into memory and then creating data of that class type?"

    If the answer is A:

    Every time you fork a wire you create a new instance of your class. Dynamic creation is anything that creates a new copy of your data -- such as Initialize Array. Now, if you need specific instances of your object that you can talk to using a reference model, then you should take a look at the ReferenceObject example that ships with LV:

    <labview>\examples\lvoop\ReferenceObject

    After you've looked at that example, you should read this discussion forum:

    The version that ships with LV is a viable implementation of references, but it is one among many.

    If the answer is B:

    Yes, there is another way to achieve dynamic loading of classes into memory, without the enum suggestion.

    Any VI that uses a class will load that class into memory. You can use VI Server to load a VI into memory even in a built application. SO....

    1. Create your classes. Let's call them Alpha, Beta and Gamma. These classes do not have to be related.
    2. Add a method named DefaultValue.vi to each of these three. This VI should have no inputs and its only output should be of type LabVIEW Object (or, if there is an inheritance relationship among your classes, any class that is a common parent to all three classes).
    3. On the block diagram of these classes, drop a constant of the class type and connect it to the output FP Terminal.
    4. Your Factory code should be changed now to use Open VI Reference. Instead of loading a class directly, it will load any of the DefaultValue.vi versions. Use a Call By Reference node to invoke the VI and get the data instance of the newly loaded class.

  15. Perhaps many of you LAVA folks out there already use that pattern in your projects. I do not believe that this is anything new, although its application to lvOOP certianly is.

    It isn't new... but you probably don't want to use it before LV8.0.

    The variant attributes have been stored using a red/black tree for O(log(n)) lookup speeds for as far back in the code as I have checked (LV6.1). I think that's how they're originally implemented. And that's good. But until LV8.0, there was a huge block of memory reserved for each attribute, which actually made this name lookup database not feasible for anything more than a couple hundred attributes. With the revisions of LV8.0, the variants have really really improved in speed (I've never benchmarked it but it's A LOT faster), and the memory overhead has decreased to almost equal to the amount of data you're storing. Variants were tweaked with this use case specifically in mind to makes it an incredibly efficient lookup tree.

  16. Starting in LabVIEW 8.0, the File>>Save As... menu option was disabled on running VIs. However, you could still open a VI Server reference to a running VI and invoke the Application.Save method to effectively perform a Save As operation. Now, in 8.2, it is no longer possible to invoke the Application.Save method on VIs that are running in any application instance, including NI.LV.Dialog, which is used for VI launched by the menu (Tools menu, for example). Attempting to do this in LabVIEW 8.2 produces an error (Error 1507. Possible Reason(s): LabVIEW: Cannot change the name or path of a VI in its current state. Method Name: SaveInstrument). I have to wonder whether there was some good reason for this (besides protecting us from ourselves). This new limitation actually prevents some cool things that we were doing with OpenG Builder -- specifically, its ability to build parts of itself at first launch.

    Doing "save as" on a running VI has always been an thread-unsafe operation. The fact that it has worked so often in the past is the Grace of God and the fact that few users have dual CPU or multicore CPUs. With the introduction of LVClasses, we had a situtation that where runtime save as would almost always crash. Couple that with the increased number of dual core and dual CPU machines, and it was more than time to do something -- either prevent runtime "save as" or make "save as" be thread safe. The latter would be preferrable from your point of view but it turned out to be technically infeasible when a couple of developers investigated it. So the error.

  17. I'm not clear about the distinction between LVOOP, GOOP, and OpenGOOP so please forgive me if I'm posting in the wrong place -- I am asking about LVOOP in LV8.2 in this posting.

    You've come to one of several right places. ;-) ni.com would also be a good place. I'm the lead developer for LabVOOP (my preferred acronym -- LVOOP doesn't have any clear way to pronounce it).

    Here are two docs that are likely to help you in some degree. Neither of these ships with LV -- they weren't finished until after the final CD was done.

    The LabVIEW Object-Oriented Programming FAQ

    http://zone.ni.com/devzone/conceptd.nsf/we...62571D800665A4D

    LabVIEW Object-Oriented Programming: The Decisions Behind the Design

    http://zone.ni.com/devzone/conceptd.nsf/we...62571CA00506D74

    I am experimenting with using LV8.2 classes to implement dynamically loaded support for devices. After a lot of frustration, I figured out how to dynamically create an instance of a class using the DefaultInstance member of the LVClassLibrary property (the hardest part was figuring out what to wire into it). I wire the path to the class definition (class_name.lvclass file) into the App->LVClass.Open method to get a reference used to generate the default class instance.

    Hrm... that's not quite right. "How to dynamically create an instance..." You're going to want to read all the sections of the above documents dealing with "by reference vs by value" and probably several of the forum topics on this website. The solution that you came to is no more nor less effective than dropping an Initialize Array primitive and asking it to create an array of arbitrarily many objects. And you really need to read up on LV2-style globals aka functional globals aka shift-register databases. I don't have links to any of those topics off the top of my head, but search around using those keywords and you'll find good stuff.

    1) How do I access the inherited data clusters from within CHILD or JUNIOR? They inherit device_name and the create_device method from Device.lvclass and I am able to invoke the create_device method (using dynamic dispatch) by wiring in an instance of Motor_Controller.lvclass or Motor_Axis.lvclass. I don
  18. Sounds great and even very simple to implement. Can we expect this to be implemented already in LabVIEW 8.2(0).1 :)

    No. Just because it is simple doesn't mean it is at all a priority. Any feature takes time, refactoring features take more time, and anything affecting the GEH takes a long long time because of the number of people who use it and have objections to any change in its behavior -- internal or external. Plus all the rest of your questions are exactly the sort of thing that would have to be brainstormed. We're a long way from even committing to something like this as a good idea, much less actually doing it.

    You should consider any significant modifications of LV using OOP to be on more of a 10 year timespan than a 10 month timespan. The stability of the whole LabVOOP system has to be verified before LV starts relying on it generally. New features can develop and dodge around bugs. Refactoring old stuff means the new has to work flawlessly.

  19. I was wrong, you cannot :)

    I think uninitialized shift registers and all other VI properties with state cause quite a lot of trouble since they don't really fit into a pure dataflow ideology. I'd like to see a possibility to have stateless VIs which could be used to implement real recursion.

    Ah, stateless VIs. I'll add your voice to the chorus. I'd love to have that, too. ;-)

    Well, whatever you decide doing please don't change anything in LV simply because it does not fit into a puristic idiology of some programming concept and/or have side effects that may be "misused" (whatever that is supposed to mean).

    Don't worry. ;-) This wouldn't affect all VIs. The idea would be a VI that you could explicitly mark as a stateless VI and thus prevents uninit shift registers, local variables, the Value property of controls and requires all controls to be a) connected to the conpane AND b) marked as requiered inputs. VIs that are stateless have some very nice properties that can be used for a variety of situations. Further the code can be optimized a lot.

×
×
  • Create New...

Important Information

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