The MacView

Virtual Instrumentation from a Mac perspective

Friday, March 03, 2006

How to Create a Reference to Data

LabVIEW is, by design, a by-value language. You can modify the data on one wire, and as long as you fork that wire before the modification, the data on the other side of the fork will not be modified. There are a few places where this is not the case, but in general, that is how things work.

There are times when you would want a reference to the data instead of passing the data directly on the wires. One reason would be to be able to reference a change in data on a completely different VI (or block of a VI). This can be done now with globals or VI server references to controls.

Another reason to have references to data is to cut down on the possibilities of copies being made of the data. I've had LabVIEW run out of memory when I had a hash table (array of cluster of 2 strings) with some large strings in them, and a lot of them. This data type was being passed everywhere, and would eventually eat up all of the memory. It didn't have to, it wasn't that big, but it was just copied everywhere. All this copying also can effect performance.

You can use a global or even a VI server reference to a control, but then you have to know how many different sets of data you are going to have. There has to be a global or a control for each different set of data. It is also not very safe if you have a large data type. For instance, with my hash table if I where to get the value out of the global, modify it and put it back into the global, someone could have replaced the data in the global before I had a chance to put the data back, and I would overwrite the other change.

How do you overcome these issues? Queues (Functions Palette -> Data Communication -> Queue Operations). To create a new reference to data, drop an Obtain Queue primitive. This would be like the new operator in C++ or Java. It allocates space for your data. The only input you need to wire is the data type (and error in if you have one). Now this only allocates the space, it does not fill it in.

To set the value pointed to by your reference, drop the Enqueue Element primitive. Wire up the Queue reference wire, the error wire and then wire your data to put into the "Reference." The Queue Out terminal will now contain a Reference to your data.

There are a couple of important things to remember about this "Reference."

1. If you fork the wire, the data will not be copied. As long as the data stays in the Reference (queue), it will never be copied.

2. There is only one copy of the data. If you fork the wire and modify the contents of the queue, you have modified the value on the other side of the fork as well.

To modify the value pointed to by the Reference (queue), you need to Dequeue the data and then Re-Enqueue it. You will also do this to read the value. First, drop the Dequeue Element primitive and wire up the Queue and Error In terminals. Take the Element output and fork it out, modify it, whatever you want to do with the value in the Reference (queue). Then when you are done, drop the Enqueue Element primitive and re-Enqueue the data (original if you are reading, or modified value if you are modifying the value).

Dequeue and Enqueue give us a protection mechanism known as thread-safety. It prevents two people from reading the data, and then writing data over the results of one another. This is important in a data-flow language like LabVIEW, in which "threading" is transparently accomplished in the language itself.

Dequeue and Enqueue also do not copy the data, but rather move the data out of the queue. This is an important speed issues when you have large data structures.

Then when you are completely done with the Reference (queue), you use the Release Queue primitive. This will release any memory used to store the data and will cause all future uses of the Reference (queue) to return errors. This is the equivalent to delete in C++ (Java has no equivalent).


Blogger Michael Aivaliotis said...

How does this approach rival the Functional shift register global? Assuming your data manipulations happen within the confines of the while loop?

Friday, March 10, 2006 3:47:00 AM  
Blogger Marc said...

One big difference is that you have to have a VI per instance of data (or a while loop per instance of data). With this approach, you can create an arbitrary number of references and, say, store them in an array.

It just gives you more flexibility.

Friday, March 10, 2006 6:39:00 AM  
Blogger odjau said...

also, it use twice less memory (there is no copy of data) !!!

Tuesday, March 14, 2006 12:02:00 PM  

Post a Comment

<< Home

The views expressed on this website/weblog are mine alone and do not necessarily reflect the views of my employer.