Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Malleable Message Data

Solved!
Go to solution

Hi, 

 

In a new project that I'm developing I've been wondering of a malleable message data where the message content can carry different types of data.

 

This project uses abstract messages and I always try to code in a more decoupled way.

 

For example, sending a boolean or an integer for a digital signal. In the receiving actor I have a type verification for converting the integer to boolean array or vice-versa. 

 

I came out with two possible solutions, variants (Data Type Parsing palette) or an object-oriented solution with typecasting.

 

In some tests I found much more convenient the usage of the Variant, because thinking in scalability, you can inject as message data practically any data type in Labview (including objects) and convert back at another end. 

 

Are there any drawbacks using variants?

 

Regards,

 

Felipe

 

 

 

 

Felipe Pinheiro Silva


Follow my blog for LV content!

0 Kudos
Message 1 of 14
(3,425 Views)
Solution
Accepted by topic author felipe.foz

> Are there any drawbacks using variants?

 

Yes. Neither of them is a showstopper, but you should be aware of the possibilities.

 

a) Performance. Lots of overhead associated with allocating a variant and copying data into it and copying data back out of it. I and a few others have done a lot of work over the years to optimize that as much as possible -- use the In Place Element structure as much as you can -- but you cannot pare it down all the way. For infrequently sent messages, it's fine. For heavily trafficked messages, it can be a problem. Still generally much faster than Flatten To String and Unflatten.

 

b) Type safety. As long as you know the pairings between your send and your receive, you can cast into and out of a variant without problem. But if you ever get off, you can have problems that will only show up at run time, something that cannot happen with the hierarchy solution.

0 Kudos
Message 2 of 14
(3,400 Views)

a) Performance. Lots of overhead associated with allocating a variant and copying data into it and copying data back out of it. I and a few others have done a lot of work over the years to optimize that as much as possible -- use the In Place Element structure as much as you can -- but you cannot pare it down all the way. For infrequently sent messages, it's fine. For heavily trafficked messages, it can be a problem. Still generally much faster than Flatten To String and Unflatten.


Ok, but how much overhead are we talking about? About the optimizations, I've really read that in lv2009 there had been a huge improvement in performance with variants.

 


 

b) Type safety. As long as you know the pairings between your send and your receive, you can cast into and out of a variant without problem. But if you ever get off, you can have problems that will only show up at run time, something that cannot happen with the hierarchy solution.


That's the idea, keeping a pattern, not anything so different. For runtime, I thought of using the error wire from variant to data to handle any mistyping.

Felipe Pinheiro Silva


Follow my blog for LV content!

0 Kudos
Message 3 of 14
(3,398 Views)

How does the overhead of putting data into/taking out of a variant compare to DD and class data accessors?

0 Kudos
Message 4 of 14
(3,382 Views)

> How does the overhead of putting data into/taking out of

> a variant compare to DD and class data accessors?

Variant is multiple orders of magnitude slower than dispatching. On common desktop hardware, one measures in microseconds, the other in milliseconds. Data accessors vary by their design -- the static dispatch, inline accessors (which is the default if you create accessors) are very good. If you remove the error terminals -- which I STRONGLY encourage -- then they are instantaneous, often optimized to zero overhead. In LV 2019, the ones without case structures got faster, and in LV 2020 they will be even more faster.

0 Kudos
Message 5 of 14
(3,365 Views)

@felipe.foz wrote:
Ok, but how much overhead are we talking about? About the optimizations, I've really read that in lv2009 there had been a huge improvement in performance with variants.

Yeah, those are the performance improvements I mentioned in my first post. Huge improvements doesn't mean they are good enough for all uses.

 

You cannot know ahead of time whether or not any given performance bound will be sufficient for your needs. So we rule-of-thumb estimate it... as I said, if the message will be heavily trafficked, I'd shy away from variants; if it is an infrequent message, I'd have no problem using it. And then you performance benchmark after the system is built, as you would for any other operation. It may be that even if it is "very slow," it still isn't the bottleneck for your application overall!

0 Kudos
Message 6 of 14
(3,362 Views)

@AristosQueue (NI) wrote:

> How does the overhead of putting data into/taking out of

> a variant compare to DD and class data accessors?

Variant is multiple orders of magnitude slower than dispatching. On common desktop hardware, one measures in microseconds, the other in milliseconds. 


That seems a bit pessimistic for Variants; I would definitely consider them to execute on the order of around a microsecond, at least for simple data contained in them.   A quick timing test of a million variants, with a mix of strings and floats, suggests sub-microsecond for both putting in or taking out of a Variant.   Don't know what a Dynamic Dispatch costs; perhaps that is nanoseconds?

0 Kudos
Message 7 of 14
(3,306 Views)


That seems a bit pessimistic for Variants; I would definitely consider them to execute on the order of around a microsecond, at least for simple data contained in them.   A quick timing test of a million variants, with a mix of strings and floats, suggests sub-microsecond for both putting in or taking out of a Variant.


That's right for variant, earlier tests also suggest me that variants are not that slow compared to Static Dispatch (inline), in fact are almost the same, considering small data chunks, in order of sub-microsseconds. That is considering SD. Inlining is really a game changer.

 

   Don't know what a Dynamic Dispatch costs; perhaps that is nanoseconds?


Just altering the terminal to Dynamic Dispatch considerably slows the conversion, in order of almost 10x slower.

 

Felipe Pinheiro Silva


Follow my blog for LV content!

0 Kudos
Message 8 of 14
(3,302 Views)

And then you performance benchmark after the system is built, as you would for any other operation. It may be that even if it is "very slow," it still isn't the bottleneck for your application overall!


Considering we are talking about microseconds or even faster conversions, I agree with you, that this conversion definitely won't be the the bottleneck of the application. Maybe not even worthy discussing performance. 😁

 

 

Felipe Pinheiro Silva


Follow my blog for LV content!

0 Kudos
Message 9 of 14
(3,301 Views)

With extremely high confidence, I state, "Your benchmarks are wrong."

I haven't seen your benchmark code. Doesn't matter. The probability that you have valid data is near zero.

 

First, the ways that most people write G benchmarks for any feature is terrible. They leave panels open, don't account for debugging options, fail to isolate from parallel effects, etc. For large operations, they suffice, but the smaller the feature being tested, the harder it gets to rule out side-effects. I don't trust benchmarks written by myself because of all the times I've been caught by something small. Benchmarks for small ops need to go through substantial peer review to be trusted. And they need to be run on multiple machine architectures to meaningfully say anything about the general case.

 

Second, I've seen benchmarks written by various programmers attempting to benchmark the cost of dynamic dispatching specifically, and they always are testing something else. Easy example: if you're seeing a 10x jump in cost, you're injecting a data copy somewhere in your test. The dispatch cost doesn't change like that.

 

Actual response I've gotten in the past: "Well, if it is that easy to inject a data copy, then that's part of the cost of dynamic dispatching." False. Data copies can occur both with dynamic dispatch calls or static dispatch calls (i.e. regular subVI calls). And both are equally common in real-world apps. When trying to decide whether or not using dynamic dispatching is going to be the bottleneck for an application, you need a test that exclusively times the actual dispatching overhead.

 

Data copying is just one example.

 

Timing something like this purely in G is extremely difficult because it is so small and because just dispatching is not isolateable as a subVI. The VI is always doing other things as part of the call (such as returning from the call), and it is the entire call that is being timed. Timing just dispatching overhead really requires C++ level checking.

 

Even after all that, what you get is still a relative measure between to systems, and there are variations in how each system can be implemented in real-world.

 

I stand by my ratios of "orders of magnitude" between the variant way and the dynamic dispatching way. Any given move-out-of-variant operation potentially runs into the need to reallocate memory, which can hang if something else has the memory manager; dynamic dispatching itself cannot. Pooled reentrancy can hang getting a new clone VI, and hang a long time if a new one must be allocated when pool is empty, but that's a separate feature. When looking at all the things that have to be accounted for in applications using these two different systems, there is far more computation and memory movement that generally happens in the variant's I-am-fully-dynamic-at-runtime-to-do-anything version than in the dynamic dispatch's I-am-compiled-to-select-from-a-specific-set-of-possibilities version. And that difference is orders of magnitude.

Message 10 of 14
(3,285 Views)