Tomi Maila
Members-
Posts
849 -
Joined
-
Last visited
-
Days Won
9
Content Type
Profiles
Forums
Downloads
Gallery
Everything posted by Tomi Maila
-
Maybe you are misunderstanding the concepts of static (strict) and dynamic type systems. Static type system is a type system that doesn't allow runtime type errors to occur whereas for dynamic type system runtime type mismatches can occur. Static type system can still be very flexible, indeed almost as flexible as dynamic type system. Type parameters allow creating statically typed functions and classes etc. with polymorphism without actually needing to duplicate code. You can for example write a QueueClass class that behaves exactly the same as queue primitive functions in LabVIEW including similar kind of polymorphism for both queue element type and queue reference type. Unlike with dynamic languages, you could never connect something with incorrect type to QueueClass methods, in exactly the same way as you cannot do the thing with LabVIEW primitives. So what you could get with proposed type parameter bases type system is LabVIEW primitive like flexible polymorphic behavior for custom developed types. Type parameters is one way to do generic programming. Quoting wikipedia: "Generic programming is a style of computer programming in which algorithms are written in terms of to-be-specified-later types that are then instantiated when needed for specific types provided as parameters." Type parameters is about specifying to-be-specified-later types defining relations between these generic to-be-specified-later types as well as setting limits to generic to-be-specified-later types. So you can do a lot of
-
Hi I posted a new article to ExpressionFlow regarding type parameters. For me it is the one of the most missed features in LabVIEW. Check out the post. Introduction to Type Parameters EDIT: Seems like the links to expressionlfow still don't work from LAVA. Please copy paste from below expressionflow.com/2009/10/23/introduction-to-type-parameters/ Tomi
-
Ideas for an IsSibling algorithm?
Tomi Maila replied to Daklu's topic in Object-Oriented Programming
Inplaceness is a competely parallel orthogonal concept. It is a compile time optimization technique used by LabVIEW to reuse buffers. The concept of inverse fully connected wires is about type propagation, if runtime output type can be guaranteed to be a function of runtime input type(s), irregardless how the VI is executed. -
Ideas for an IsSibling algorithm?
Tomi Maila replied to Daklu's topic in Object-Oriented Programming
Is there official NI terminology for the concept I called fully connected? What do you think should be the best term for the concept? Somehow it resembles mathematical concept of connected manifolds. Maybe reverse or inverse connected could be more clear. They somehow tell that input need not always terminate to output terminal but instead output terminal must always originate from input terminal. -
Automatic type casting doesn't work with DVRs
Tomi Maila replied to Tomi Maila's topic in Object-Oriented Programming
I could extend my wish a little to apply to the use case of constructing LVOOP DVRs. If input is a value class and output is a DVR of the input, then tha automatic casting would be applied as well. -
Automatic type casting doesn't work with DVRs
Tomi Maila replied to Tomi Maila's topic in Object-Oriented Programming
Ok. Makes sense. -
Hi, Any specific reason behind applying DVRs to LVOOP why automatic type casting of wires originating from class type output terminals is not applied to data value references of class types. For static dispatch class method VIs, automatic type casting of class types occurs if wire from input terminal to output terminal is fully connected. Path is fully connected if all of the data paths that lead to the class output terminal originate at the class input terminal. If there are subVIs on any of the routes, the the subVIs must also be fully connected. All methods with dynamic dispatch input and dynamic dispatch output are always fully connected. The current design makes usage of DVR class output terminals rather useless.
-
Ideas for an IsSibling algorithm?
Tomi Maila replied to Daklu's topic in Object-Oriented Programming
No, not really because using dynamic dispatch initializer allows you to outsource the task of initializing a set of different classes of objects to some other method. For example you could have different kinds of binary streams such as binary file stream or network binary stream. Setting up binary stream parameters can be separated from the actual opening of the binary stream You can for example specify the file path to use for the file binary stream or specify the network address to use for the network binary stream. Then you can pass either of the stream specifications to a method that actually opens the stream using dynamic dispatch open method. I think this is kind of an initialization phase for the streams. And the open method can even take parameters such as input vs. output stream. My constructors look most of the time like this. The terminals on the left are for construction parameters. In the example case there is only one, the States terminal. The terminal on the right returns the constructed object. The terminal on the top (right) is an optional terminal for the child object. It should not be used unless the constructor is called from within child constructor. It should always be used within child constructor. When you pass in a child object to the parent constructor, the parent constructor naturally returns the same object type that you pass in. Or it should. It returns the child type object, if your class in input terminal is fully connected (see glossary below) to the class out output terminal. There may be unbudnle/bundle nodes and other fully connected class methods on the route of the wire. However you cannot have things like class constant wired to the output terminal on any possible route. An image should tell more than 1000 words. An example of a valid fully connected class method is below. You can test if your class method is fully connected by making both class in and class out terminals dynamic dispatch. If the VI gets broken, then it is not directly connected. Only directly connected VIs can have dynamic dispatch input and output. Glossary Fully connected: All of the data paths that lead to the class output terminal must originate at the class input terminal. If there are subVIs on any of the routes, the the subVIs must also be fully connected. All methods with dynamic dispatch input and dynamic dispatch output are always fully connected. -
Ideas for an IsSibling algorithm?
Tomi Maila replied to Daklu's topic in Object-Oriented Programming
But if I use dynamic dispatching, the input is required, and I have to wire an object in - is there a way around this? Honestly guys, why the heck do you want to use dynamic dispatching for create/constructor methods. The only reason to use dynamic dispatching is that you wire something into a method and you don't know what exactly is that something. And therefore you let the method to be chose at runtime. When you create an object, you always know what you want to create, at least you should. If you don't know what you are creating, then you should use a factory pattern or similar. But I think constructors should always be static methods and call static parent constructors. So if you have a class My Class, create method should be called Create My Class and it should be static dispatched. It should have class input terminal only for a single reason, so that child constructor can call it. I myself usually make the class type in terminal even optional, to make clear that is should not be used in anywhere else but when child constructor is calling the method. Tomi -
Ideas for an IsSibling algorithm?
Tomi Maila replied to Daklu's topic in Object-Oriented Programming
You can resolve this issue using property nodes Application:All VIs In Memory or maybe also Application:All Libraries in Application Instance together with Preserve Run-Time Class primitive to resolve the class hierarchy in memory. The solution goes as follows. For each class or class private data control VI in memory, load the class default value. That can be loaded with Get LV Class Default value VI and also if I remember correctly, some scripting node returns it. Then construct a hierarchy tree of the classes in memory using Preserve Runtime Type primitive. This needs to be done only once unless you dynamically load classes during the runtime. Once you have the class hierarchy, your sibling test is trivial. Tomi -
LabVIEW constant values change
Tomi Maila replied to Tomi Maila's topic in Object-Oriented Programming
You are mixing two terms namely value and default value. One can set the value of LVOOP constant by simply pasting (right click -> data operations -> paste ) a value to it at development time. Before pasting a value to it, you need to copy (right click -> data operations -> copy) the value from somewhere else such as a LVOOP indicator where you have written a value by executing a VI. Default value of LVOOP constant is something set by the class developer and it is the initial value of the LVOOP constant when you drop it to block diagram. -
LabVIEW constant values change
Tomi Maila replied to Tomi Maila's topic in Object-Oriented Programming
Would you ban class placing controls and indicators as well on the front panel? They are on the same role here. -
LabVIEW constant values change
Tomi Maila replied to Tomi Maila's topic in Object-Oriented Programming
If the class developer changes the private data default value, the constant on the class user's application gets reset under the above described conditions. -
So why don't we simply automate the creation of the dynamic dispatch methods corresponding dynamic dispatch by-value method by scripting. Say you could have a virtual folder in your class and a scripting tool that would automatically synchronize the folder content so that it would create a corresponding DVR method for each non-override dynamic dispatch method in the folder. Tomi
-
So you would always, when using DVRs, place a semaphore to the class on the top of the hierarchy and when ever accessing any data in the hierarchy you would lock the whole hierarchy using semaphore? Why you would not rely on the IPE structures when ensuring transaction atomicity? Sometimes we just need to place class methods inside the locking mechanism. If semaphores are used for locking and not IPE, then we can place class methods outside IPEs. Otherwise there are situations where we simply need to place the inside the structure.
-
I am joining this discussion quite late but here is my 5c. LabVIEW is a parallel programming language. DVRs allow accessing shared data across parallel processes. Now the most important thing we need to pay attention to is to make sure that in no case, the data referenced by DVR is in a temporary state. That is state transitions of the DVR referenced data must always be atomic. There should never be an operation where operation leaves a DVR referenced state into an intermediate temporary state even for a fraction of a second. If the DVR referenced data can at any time be in an intermediate state, then there is always a possibility that a parallel process accesses the data at that moment and this results in the application malfunction that is close to impossible to debug. So how this relates to LVOOP by-ref design patterns? Well, it means that the lock for the DVR data across all the inheritance hierarchy must always be kept for the entire period of the whole atomic transaction being processed. Let's consider the consensus solution on this thread of adding a DVR into the private data of a class. Say class Parent private data contains DVR of some cluster data. Class Child inheriting from Parent contains another DVR of another cluster data. This is in accordance with Kurt's (SciWare's) examples. The problem with this scheme is that DVR's are accessed using in-place memory structure (IMS). In this scheme of holding DVR in classes private data, IMS does not allow us to lock both child DVR and parent DVR at the same moment. As a result we cannot guarantee atomicity of a transaction. Let's assume Parent has two data fields A and B with initial values a0 and b0. It is not possible for a child method to execute the following sequence as an atomic transaction: read field A of parent data by executing a parent data accessor method Read A, get value a0. do some data processing based on the parent data a0 read on step 1. modify parent private data field B by writing a value b1 using a data accessor method based on the result of processing of step 2. At the end of step 1, the parent data becomes unlocked again as the IMS locking the parent data is within the parent accessor method. As a result a parallel process can modify the parent data during step 2 destroying the atomicity of the process 1-3. Say a parallel process can modify parent field A during step 2 to be a1. The parent data is no longer in the state the child method assumes it to be. The final state of parent data would be (a1,b1) and not (a0,b1) as expected by the process 1-3. The state (a1,b1) may not at all be a valid state of the object. As a result of our implementation our object state have become invalid representing possibly something unreal. You can also have dead-locks with the concensus implementation. Assume a parent method locks parent data, then calls calls a dynamic dispatch method of the parent class that has been overridden by the child class and finally writes data back to DVR unlocking the data. Now the parent method is already locked at this point. The child override method may then try to relock parent by calling parent accessor method. However parent is already locked and dead-lock occurs. Dead-lock can occur even if child method doesn't call parent method. A parallel process may be trying to lock first the child and then the parent data in inverse order. As the order of the locking is inverse from the order of the locking of the first process, we may end up in a situation where one process has locked parent and is waiting for access to child data and another process has locked the child and is waiting an access to the parent data. Dead-lock. So I would not recommend embedding DVR's to classes private data. Instead I would make all atomic methods to contain DVR's to the class on the input and output terminals, lock the entire hierarchy by a single IMS and do all internal processing using by-value wires. You get the performance of by-value classes. You get the benefits of LV natural parallelism within the class methods. And you can always guarantee transactionality of the DVR referenced state. Conceptually one can consider DVR referenced states as functional globals with a reference to particular subVI clone instance holding the shift register with some state. As we functional globals, we do not want parallel processes to be able to modify the state at the same time. With DVRs only one transaction VI may be modifying the DVR state at any moment of time. With functional globals only one case of the functional global may be executing at any single moment of time. So when using DVRs, make sure you always modify the DVRs transactionally a singe VI at a time. You need to lock the entire data hierarchy in a single transaction to guarantee this behavior. Well, at least immutable variables could be used as well, where the variable is never written. For example a file class could contain a path field in addition to file reference. A path field for the file path remains the same until the file is closed, so it would not cause even if it is not a reference. And accessing file path would not require locking any DVRs.
-
Can you Stephen please update the document to contain reasons why DVR terminals do not support dynamic dispatching?
-
I found a problematic issue with the behavior of LVOOP class constant values. LVOOP constant values get cleared when the default default value of the class private data is changed to the same value as the constant value. Let's make some definitions: Class default value: The default value of class private data Default value class constant: LVOOP class constant, that has a specific default value flag. The constant behaves as by-reference object that refers to the class default value global. When the class default value is changed, the default value class constant is changed in synchrony. Non-default value class constant: LVOOP class constant with non-default value. The value behaves as any by-value object. It has its own value that does not depend ony the default value of the class, except in the problematic case described below. The LVOOP class constant can get a non-default value when you paste a previously copied value to it using Data operations -> Paste from shortcut menu. Class developer: An organization or developer that is responsible for developing and maintaining a class. Class user: An organization or developer that is using the class developed by some class developer. Consider National Instruments (class developer) has developed a class representing an IP connection to some IP address. Internally the class represents the IP connection with a IP address string constant in class private data. NI has initially set the "default value" of the class private data to be 0.0.0.0. Assume you (class user) as a non-NI developer have used a class constant of this connection class labeled loop back on block diagram and the value of it is non-default 127.0.0.1 representing a loop-back connection. Now NI decides they want to change the class default default value from 0.0.0.0 to 127.0.0.1. The issue here is that this change by NI will clear the value of your non-default value block diagram class constant and replace it by a default value class constant.Now NI decided that 127.0.0.1 is not a good default default value for the connection class and sets it to be 0.0.0.0 again. How this affects your program. Well, LabVIEW will not recover your initial value to your loop back class constant, i.e. 127.0.0.1, but instead keeps the default value. As a result, your loop back class constant no longer represents a loop back connection but a connection to non-existing IP-address 0.0.0.0. The behavior I expect would be that the value of loop back class constant on block diagram would always remain 127.0.0.1, even when the class private data default value is changed to the same value. You can replace the roles NI and you with you and your client. The issue here is, the constant values should never be cleared. If they really need to be cleared for some reason such as the class private data having changed structure, LabVIEW should definitely warn the developer that the constant values may no longer represent the data they used to. What happens here really is that when LabVIEW notices that a non-default class constant has the same value as the default value of the class, it replaces the constant value with a reference to the default value object. So the constant no longer is a constant representing its initial state but becomes a constant that stays in synchrony with the default value object. Once the default value object is changed, the constant value will also also changed. I think that LabVIEW should follow the principles below Theorem 1: A block diagram constant with a non-default value should always represent the same state that it initially had, irregardless of what changes has been made to the default value of the private data. Initially a red apple should always stay a red apple and not implicitly become a green apple even though the default color of apples is changed to green. This is what is currently happening in LabVIEW. Apple block diagram constants explicitly specified to be red apples are turning into green apple constants under certain changes of the default value of the class private data. Theorem 1 is broken by the fact that class constant state can change from red to green apples. Theorem 2: Initial and final revision of a class A,call them A1 and A3, are identical, but an intermediate state of class A, call it A2, is not identical to A1 and A3. Code compiled using class revisions A1 or A3 should execute identically irregardless if the code has been compiled against intermediate state A2. Theorem 2 is broken as you can revert the class to its initial state, still the changes in the class between initial and final state change the code using the class. Theorem 3: There should be no difference if code using a class has or has not been in memory when editing a class, if the class interface does not change.Once the code using a class is loaded in memory together with the latest revision of the class, the code should be updated always to the same state. Theorem 3 is broken as if code has been loaded in memory when a private data default has been changed twice, then the state of the code is different than the state of the code that has only been loaded into the memory when the class has reached its final state. So what I propose is that a class constant always has either a default value or a non-default value. The non-default value can equal to the class default value, but as far as the constant is concerned, the value is still called a non-default value. That is the value of a constant does not change from a by-value state to a by-reference state, unless explicitly changed by the class user. When the default value of the class private data is changed, the values of block diagram constant remain untouched. That is constants that already contain default value still contain the default value in the future. The constants that contain non-default value still contain a non-default value even if the new class default value equals to the constant non-default value. The important thing here is that the constant with non-default value has been explicitly set by the developer to be at some state and this state should be persistent no matter what (possibly another) developer does with the default value of the class private data. Once you paste data to a constant, it automatically gets a non-default status. The same would apply to indicators, once LabVIEW have written data to an indicator(control), the value of the indicator (control) would be non-default irregardless if the value actually equals to the class default value. Only when you want to clear the value back to the class default value, there could be a right click menu option Data Operations -> Reset to class default value. Let us take still another example of the current behavior. We are developing an natural number class. Initially we set the class default value represented by integer to be 0. We add a method +1 to the class and implement it by adding a class constant with non-default value 1 together with input data using another class method Add. We then accidentally change the default value of the class private data integer to 1. Accidentally! This clears the non-defaultness from out constant on +1 method block diagram. The constant gets the flag default value. Now we immediately notice out accidental mistake and correct the mistake by returning the default value of the class to0. Immediately. But the harm has already been done. The +1 method no longer functions as expected but now becomes +0 method instead. Is this really what we want? No warnings. Nothing. Just somewhere under the hood things implicitly change and we know nothing at all that we just broke our code. Everything that here applies to class constants applies to class controls and indicators, I think. I want to raise discussion. Should current behavior be changed in LabVIEW or do you think the present behavior of class constants is correct?
-
Mary Fletcher from NI R&D explained the implementation of the parallel for-loops in more detail in the comments of the blog post. The number of loop instances specified in the loop configuration dialog is the maximum number of workers that could work on parallel for executing the loop iterations. The actual number of workers is specified at runtime to be the value of P terminal, if it is smaller than the maximum number specified in the configuration dialog.If P is greater than the maximum number, then the maximum number of workers is used instead. If P is not connected, LabVIEW uses as many parallel workers as there are logical processors in the machine,however never exceeding the maximum number of workers specified at runtime. If there is a parallel loop within another parallel loop, only the outer parallel loop will be parallelized. This will change in LabVIEW 2009 SP 1 where LabVIEW will parallelize both loops resulting in P*P' workers for the inner loop. This limitation of parallel loop within another parallel loop does not apply to subVI calls within parallel loop subVIs having parallel code themselves. If the number of workers specified at configuration time and at runtime both are equal or grater than the number of iterations, all the loop iterations will then execute truly in parallel and you can safely use design patterns such as producer-consumer pattern between loop iterations. Thank you Mary for this valuable information.
-
Thank you
-
Hi, If I try to post a link to my blog, expressionflow.com, LAVA adds extra mark-up code <b></b> within the link, that breaks the link. Tomi
-
It is just a random number much enough greater than the number of development time specified loop instances, which in my example was 10. Practically I am requesting the 20 iterations of the for loops to be executed by 20 parallel workers. If the iterations would really be all parallel, then there would not be dead-lock. Please check the comments on the blog post for Marys more detailed insight.
-
Seems like LAVA is censoring expressionflow content. So just type expressionflow.com on the URL bar.
-
Since I founded Agile 4 in the beginning of the year, I have not had much time for LabVIEW development and my blog on ExpressionFlow. Recently I have had some time to get myself familiar with the new LabVIEW 2009 features and I really hope I will be able to again use LabVIEW as much as possible. I just posted a new post Limits of for-loop parallelism to ExpressionFlow, for the first time after a short silent period. I try to keep up posting interesting stuff more regularly in the future. Cheers, Tomi
-
Anyone running LV2009 and Vista Ultimate 32bit
Tomi Maila replied to Anders Björk's topic in LabVIEW General
Just to report that I am running LabVIEW 2009 on 32-bit Windows 7 Ultimate and initially at least it seems to be also working. I haven't tried most of the feature though