Friday, August 24, 2012

What's the Deal with Coercion Dots?

Let's talk about those little red dots on your diagram:

They used to be gray, but we changed the default color of coercion dots to red in LabVIEW 8.2, presumably to make them easier to see (and so their appearance wouldn't be confused with the Show Buffer Allocations feature).  Note that you can change the color of coercion dots in Tools > Options > Environment > Colors > Coercion Dots, but most people keep them red.

So what is a coercion dot, anyway?  It is LabVIEW's way of telling you that you have mismatched data types wired together, but they're close enough that your VI will still compile.  Contrast the image above with this one:

In the first image, you have two (different) numeric types wired to the Add function. Since both types are numeric, the Add will work, after it coerces one of the inputs (the I32) to match the other (the DBL).  In the second image, you're trying to add a numeric and a path.  Those types are coercion can be performed, so you get a broken wire.

The purpose of the coercion dot is to inform you, the programmer, when LabVIEW is performing a conversion behind the scenes.  You could explicitly perform this conversion yourself, thereby removing the coercion dot entirely:

Scalar Numeric Coercions
So how does LabVIEW decide which type will be coerced?  In the case of DBL vs. I32, it's rather straightforward...the I32 will be coerced, because coercing the DBL would lose fractional data.  Depending on which value is coerced, 1.7 (DBL) + 3 (I32) would either equal 4.7 (DBL) or 5 (I32)...I think we all agree that 4.7 is the better answer. :)

So for coercions like I32 -> DBL, you don't really need to worry about the coercion dot.  You could add the To Double Precision Float conversion function (these are often called conversion "bullets") to your diagram like the previous screenshot, but it's just going to do exactly what the coercion dot is already doing.
But what about trickier numeric coercions?  Check out this diagram:

I have indicated the data types of the constants in their labels.  In this diagram, the I16 has been coerced to a U16 value.  But since the result of this operation is a negative number, the U16 rolls under, and this diagram generates a value of '65531', even though you probably wanted it to generate '-5'.  So this would be a case where an explicit conversion on your part (specifically, of the U16 to an I16) would have been preferable.  When dealing with integer data types in particular, you'll always want to keep an eye out for places where roll unders or roll overs might occur.

One nifty feature to mention here is the Adapt to Source right-click menu option on numeric indicator terminals:

If you do convert the U16 input to an I16, and you have Adapt to Source enabled on the indicator, then its data type will automatically change to an I16 once the types match.

Array Coercions
A lot of the paranoia surrounding coercion dots is the concern that they "affect memory usage".  In practice, this often turns out not to be true.  Check out this diagram:

Now, the "coercion dots are bad!" crowd would tell you that, in order to avoid additional memory usage because of the different data types, you need to do something about that coercion dot, particularly if this loop is running a whole bunch of times (since the arrays might be very large).  Well, it turns out that, no matter how I try to "optimize" this diagram to remove the coercion dot (a few things I tried included using a conversion bullet on the iteration count, a conversion bullet on the I32 array, and maintaining my own DBL iteration count in a shift register), all other approaches used more memory during execution than the simple coercion dot approach illustrated here.  So before you go trying to get rid of every coercion dot on your diagrams because you're afraid they affect performance and memory, I'd recommend actually using the Profile Performance and Memory Window to see if any of the alternative approaches really are better.

Typedef Coercions
Another thing to look out for is coercion on typedef terminals. In these cases, the data types contained in the wire might be the same, but if one end of the wire is typedefed, and the other is not, you'll get a coercion dot.  In general, you're going to want to make sure this does not happen, and the easiest way to do so is to ensure that the source and the sink of the wire are both linked to the same typedef.  If you do not do this, and you see a coercion dot on a typedef terminal, then you may have problems when you change the data type of the typedef...the typedefed terminal will change, but the other terminal will not.  In the case of types like clusters and enums, you'll get a broken wire when the typedef changes. If you have many non-typedefs wired to typedefs throughout your VIs, these broken wires can become tedious to correct.  The easiest way to avoid these kinds of problems is to make sure the terminals wired together are linked to the same typedef.  For more information about typedefs, check out this Eyes on VIs post.

Variant Coercion
Another time when you'll frequently see coercion dots is when you're dealing with variant data types.  Since a variant can contain any LabVIEW data type, you'll often see APIs where you will wire a specific type into a variant terminal, and you'll see a coercion dot.  Again, this is perfectly fine, and it's usually a waste of diagram space to use the To Variant function to eliminate the coercion dot.  The only time I can recall using To Variant was when I had a case structure generating a variety of types in different cases, all of which went to the same (variant) output tunnel.  In this case, I had to use To Variant to avoid assigning a specific, non-variant type to the tunnel:

Identifying Coerced Types
If you want some more details about the specific types that are causing a coercion dot, there's a great new feature in LabVIEW 2012 that will display (in the Context Help window) the wired type, and the expected type, of a coerced terminal:

There are some other interesting scenarios involving coercion dots, like Dynamic Data, LabVIEW class wires, strict VI Server reference types, and more.  But for now, I'm hoping this blog post gives a good introduction to some more general issues regarding coercion dots.  I have described several scenarios that you need to watch out for...but these tend to be corner cases.  Usually, LabVIEW is doing the right thing behind the scenes when it has to coerce data types, and you won't need to worry much about memory usage, performance, etc., as long as you pay attention to the types you're using in the first place.