Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Use of an abstract Message class to remove the "To More Specific Class" boiler-plate code in Message Class's Do.vi

Hello

My apologies if this has been discussed before however;

I have been getting to grips with the Actor Framework for a little while now and, having looked at various examples, I am curious if the approach I have been taking is valid or frowned upon by other AF users.

For each Actor, I create an "Actor XYZ Message" class that inherites from Message.lvclass.

In Do.vi of the new class, I perform the cast from Actor.lvclass to Actor XYZ.lvclass and then call "Do Core.vi" (which has the same connector pane as Do.vi except that it has Actor XYZ.lvclass inputs and outputs)

Any messages intended for Actor XYZ can then inherit from Actor XYZ Message.lvclass and are forced to overwrite the Do Core.vi.

Just in case that is a little confusing or poorly explained, I have attached a simple example project.

I do this to avoid the boiler-plate "to more specific class" code that would have to be reproduced in every Do.vi, and, if I wanted to, I could have some code execute when each message is received by the Actor XYZ core (usefull for debugging/logging etc).

So AF users, is this what you do? Should I avoid it?

Cheers

John

Message was edited by: John_Neutron Updated Example Project.zip to demonstrate Message Enqueuer Wrapper.

0 Kudos
Message 1 of 30
(12,103 Views)

TL;DR: No, I don't do this currently, but perhaps should reconsider doing so in the future.

Longer:

Doing that does work. I do not do that. My reason is simple: It adds another essentially useless class to the hierarchy and hurts performance.

You cannot inline dynamic dispatch VIs. So when we dispatch to Do.vi, that's one hit. When you dispatch on Do Core.vi, that's a second hit. What have you saved? Just writing the To More Specific node. That node is generated by scripting in 99.9% of my messages. Why do I need another hit?

Now, there is one good argument for doing this: Creating a common parent class for all messages intended for Actor XYZ means you can identify all the messages in memory that can be sent to a given recipient. I like that argument, so I wouldn't object to someone doing it, but I've not done it in any of my code.

It might be good for me to re-evaluate this practice. When we first started on the AF, I was paranoid about performance. We were creating this messaging system with all these moving parts with no real idea what the performance characteristics would be when it hit real hardware. So anything that introduced delays was cut unless it had overwhelming positive value. We now have a better sense of the performance of the AF, and it scales much better than I had dared to hope.

Note: You'll find many programmers who are always opposed to any code that benefits the code developer at the cost of the code user. I'm not in that camp at all -- modern hardware gives us lots of performance leeway in most apps before a user would notice -- but I am cautious with such decisions. And some folks do need to bleed every line of performance from a system.

Message 2 of 30
(4,315 Views)

John_Neutron wrote:

So AF users, is this what you do? Should I avoid it?

In my opinion, and assuming your app can tolerate the slight performance hit, this falls in category of personal preference.  If it makes more sense to you to do it that way, go ahead and do it.

0 Kudos
Message 3 of 30
(4,315 Views)

Hi John,

I really like your suggestion.

I've done several smaller and one bigger project using the Actor Framework and it bothered me that there's no design protection against sending wrong messages. LabVIEW is type-checking so many things at so many places during design (you immediately see if your code does not compile), but erroneous messages can only be seen at run-time - and if not handled properly they even stop your actor.

So far, I only added an error checking layer to the concerning actors, but your idea introducing another class is way better. I don't (really) agree with the performance hit since dynamic dispatch is not that costly (although I'm also kind of performance freak).

thanks & cheers,

Ben

_________________________
CLA
0 Kudos
Message 4 of 30
(4,315 Views)

Ben,  Like you I bemoan the lack of strict typing of message senders.  Although most disagree with me I find it a fatal flaw of the framework.

Admittedly, it's not an easy one to fix but I feel there needs to be edit-time type checking done in order to keep things all correct.

Shane.

0 Kudos
Message 5 of 30
(4,315 Views)

Hello

I am glad that my point lead to some interesting discussion and thank you everyone for your contributions.

It is also reassuring to hear that the performance hit is not too bad, and for the time being I will probably contiune on with this practice. One of the good things about the AF is the freedom it provides the developer to make decisions over performance vs ease-of-implementation.

As a suggestion regarding the typing of Message Enqueuers, if you wanted to ensure that it was reasonably difficult to send the wrong message to the wrong actor, have you condsidered creating an Enqueuer Wrapper Class which wraps the key functionality of the Message Enqueuer Class?

You could then inherit from this for each Actor i.e. to create an Enqueuer for Actor XYZ. Then use this class as the Message Enqueuer inputs on your Send VIs in each message class. I have modified the code I attached to my initial post to demonstrate this.

Cheers

John

0 Kudos
Message 6 of 30
(4,315 Views)

Dynamic dispatch has a fixed overhead of somewhere around 1 us per call depending on your system. More discussion of that at the following:

https://decibel.ni.com/content/docs/DOC-13709#/

https://decibel.ni.com/content/thread/19028

http://forums.ni.com/t5/LabVIEW/Big-Performance-Degradation-in-LabVIEW-2012/m-p/2699881#M801792

I would guess that usually this isn't an issue for AF messages. I don't think I've ever felt the desire to handle 1000 AF messages in 5 ms before. But you should keep it in mind. I wonder what the overhead would be on a typical single-core cRIO controller...

0 Kudos
Message 7 of 30
(4,315 Views)

John_Neutron wrote:

As a suggestion regarding the typing of Message Enqueuers, if you wanted to ensure that it was reasonably difficult to send the wrong message to the wrong actor, have you condsidered creating an Enqueuer Wrapper Class which wraps the key functionality of the Message Enqueuer Class?

I have. The first problem is generating the actor -- you have to wrap Launch Actor and then wrap its return value. You can't guarantee that the actor will always be launched through your wrapper.

But assume that you're ok with that. Then you have to decide what kind of wrapper to write.

Option 1: A wrapper that does not have a generic Enqueue.vi that can take any message.

In this case, you only have Send methods directly on your wrapper class for sending the specific messages you want the actor to receive. The major downside is that this puts the actor author right back in the position that I've been trying to get them out of -- having to fully specify all possible atomic actions on the actor. The actor author generally does not have sufficient knowledge of the calling contexts to know the operations that need to be handled as a block. [I know Daklu and others disagree with me on this point, but this is my observation.] The actor author specifies what the actor can do through the public API of the class -- the caller is then responsible for invoking that API by sending messages. Those messages usually represent calls to a single function, but the ability to call multiple operations is critical, IMHO. The proper assignment of responsibility for atomicity is the sender of the message, not the receiver.

Option 2: A wrapper that has a generic Enqueue.vi along with specific Send methods.

This is the better option for me, but in general people who want the wrapper class are doing it because they want to rule out any other possible messages, and having the generic Enqueue defeats that goal. But then, if you have my philosophy, deciding what messages to send is the caller's choice. Ideally, a message could know its intended recipient and the pipe would only accept messages with that tag, and really ideally that tag would be checked mostly at compile time (leaving door open for dynamic messaging systems). We don't have such a mechanism in LV at this time. Would be nice.

0 Kudos
Message 8 of 30
(4,315 Views)

Intaris wrote:

Ben,  Like you I bemoan the lack of strict typing of message senders.  Although most disagree with me I find it a fatal flaw of the framework.

Do you know of any LV messaging framework which has that ability? To me, "fatal flaw" means a reason to choose anything else over the thing with that flaw. But if every messaging system has that flaw, I don't see how that becomes an evaluation criteria.

0 Kudos
Message 9 of 30
(4,315 Views)

Well for me it's fatal because it just rubs me the wrong way.  Perhaps the choise of the word "fatal" was a bit exaggerated... I'll definitely concede that.  Perhaps "niggling" would have been better.  A niggling flaw.

I find the answer (Option 2) in your previous mail with the messages knowing where they need to go to be the best solution.  I also arrived at this conclusion when looking at ways to do what I wanted (which I wasn't able to figure out).

0 Kudos
Message 10 of 30
(4,315 Views)