Jump to content

Master/Slave vs Producer/Consumer templates...?


Daklu

Recommended Posts

Definitions:

Master/slave - NI's template implementation.

Actually you can. Set the queue size to one and use the lossy enqueue function.

Incorrect. This only works if you have ONE dequeue place. If you were to try this with multiple "slave" loops it would immediately break down. A queue (even size one) will not perform identically to a notifier.

So I'll ask my original question again:

Does anyone use the master/slave template pattern? You MUST use a notifier as the transport. You MUST have atleast 2 slave loops. You must RELY on lossy transmission (ie its a better solution than a lossless transport.) If that's not the case I won't be satisfied as it is just a different way to solve a problem that could be done using a different, better, pattern.

Basically I think the template is terrible, and its actual use cases limited. Using a notifier for commanding one "slave" is a drop in replacement for an SEQ (What Daklu was talking about) solution. For whatever reason NI has labeled it a "fundamental" concept, but it is really just academic.

~Jon

Link to comment

Would you care to take a stab at enumerating your understanding of those differences?

Off the top of my head....

P/S : Single server to many clients, Single clients to many servers. (An example of Many to Many).

P/C: Single server to single client (An example of 1-1)

M/C: Single Server to many clients , Single clients to single Server (An example of 1-Many).

Aggregator:Many servers to Single client, Single client to single server (An example of Many - 1)

The list goes on but are variations on something to something and these too can also be broken down further.

That's my understanding at least.

(Oxygen is starting to get a bit thin up here :D )

  • Like 1
Link to comment
Master/Slave, Producer/Consumer, Observer/Observable, Publisher/Subscriber, etc. describe the relationships between parallel processes, not the implementation. The transport mechanism is an implementation detail and entirely incidental to the relationship. No doubt it is a very important detail, but it doesn't affect the overall relationship between the processes.

I agree, there are more significant differences than communication method. A "server" stands ready to respond to requests from a "client"; a "subject" notifies its "observers" of what it's doing (but is not acting on requests and is entirely unaffected by the observation). A "slave" serves, and is entirely dependent on, one master. "Producer" and "consumer" are dependent on each other and have no separate identity.

There all essentially pieces of code exchanging information, but the particular metaphors chosen carry useful, if imprecise, meaning.

-- James

  • Like 1
Link to comment

Incorrect. This only works if you have ONE dequeue place. If you were to try this with multiple "slave" loops it would immediately break down. A queue (even size one) will not perform identically to a notifier.

Only if you dequeue. Preview Queue Element would be used in this case.

  • Like 1
Link to comment

Definitions:

Master/slave - NI's template implementation.

Hmm... I'm not quite sure how to interpret this statement. Are you saying NI's template defines the what the master/slave pattern is, or are you saying your comments have been directed at that particular implementation of the pattern? The context of your posts suggests the former, so I'll assume that is what you mean, but to be honest it's not clear to me. Let me know if I've misunderstood you.

Incorrect. This only works if you have ONE dequeue place. If you were to try this with multiple "slave" loops it would immediately break down. A queue (even size one) will not perform identically to a notifier.

Did you see the part where I said each slave loop gets its own queue? This is what I'm talking about.

post-7603-0-48408300-1319152627_thumb.pn

Now I thought I understood the differences between queues and notifiers, but maybe there's something I'm missing. As far as I know these two master/slave implementations are functionally equivalent. Is there an edge condition I'm not aware of that makes them behave differently?

Basically I think the template is terrible, and its actual use cases limited.

I agree it appears to be pretty limited. I suspect it's designed for beginners and those who are using Labview as a data collection tool rather than as an application development tool.

A "server" stands ready to respond to requests from a "client"; a "subject" notifies its "observers" of what it's doing(but is not acting on requests and is entirely unaffected by the observation). A "slave" serves, and is entirely dependent on, one master. "Producer" and "consumer" are dependent on each other and have no separate identity.

Ahh... at least one person agrees with me on what it means to be a slave. (Maybe we can brainwash convince the others. :ph34r: ) Your description of p/c doesn't quite make sense to me, but that's another discussion.

Only if you dequeue. Preview Queue Element would be used in this case.

Jon is right. Preview leaves the message on the queue, so if a slave is running faster than the incoming messages it will preview a message it already acted on.

Master-Slave Comparison.vi

  • Like 1
Link to comment

Jon is right. Preview leaves the message on the queue, so if a slave is running faster than the incoming messages it will preview a message it already acted on.

You need to just code it with both. They don't behave the same, preview runs away. Make like 3 slaves operating on a notifier, and then 3 on a queue. they will not produce the same results

Gak! You're right. That's what I get for multi-multitasking. :wacko:

  • Like 1
Link to comment

Hmm... I'm not quite sure how to interpret this statement. Are you saying NI's template defines the what the master/slave pattern is, or are you saying your comments have been directed at that particular implementation of the pattern? The context of your posts suggests the former, so I'll assume that is what you mean, but to be honest it's not clear to me. Let me know if I've misunderstood you.

I'm justing trying to make a distinction between the NI template (which I've said before is terrible) and the code you are actually writing (which is actually a good example for people.)

I don't wanna get hung up on the vocab, it is just irritating to me that there is this template out there in the wild called "master/slave" which noone uses and is continually talked about.

At any rate, here is some xnode magic I've been working on. I have no idea when I'll finish it, but it is in working order as of today. It fits fairly well in with this discussion anyway.

It needs to be optimized for recompiles and more thoroughly tested.

Disclaimer:

Don't use this for anything, its terrible.

It will crash labview (not really, but maybe, its not been tested enough to know for sure.)

If you still want to look at this, look at the .lvproj in the example folder.

It will get you started. The "catch.xnode" is worth your time i think.

Observer Pattern.zip

Edited by Jon Kokott
Link to comment

I'm justing trying to make a distinction between the NI template...

My mistake then; I apologize. I thought you were asserting notifiers and lossy communication are inherent parts of the general M/S pattern instead of just components of that particular implementation.

I don't wanna get hung up on the vocab, it is just irritating to me that there is this template out there in the wild called "master/slave" which noone uses and is continually talked about.

The older I get the more I find defining the vocabulary up front saves a lot of misunderstanding down the road. (Though hopefully I'll never get to the point where I ask someone to define 'is' for me.) I've seen a reasonable amount of talk about master/slave patterns, but I've never taken that to mean they used this template as a starting point. I don't hang out on the dark side much... maybe they talk about it over there?

At any rate, here is some xnode magic I've been working on.

I took a quick look, but to be honest I know very little about creating or modifying xnodes. I'd love to explore using them for collections... so many things to do... *sigh*

(It appears I don't have the correct license for working on xnodes anyway...)

Link to comment

I took a quick look, but to be honest I know very little about creating or modifying xnodes. I'd love to explore using them for collections... so many things to do... *sigh*

I'm not sure an xnode will be suitable for a collection. You have to know everything about the type in the wire and you'll be stuck with the type after you create the collection. I think you can do what would be useful just using standard class programming, you'll just get a ton of coercion dots.

Edited by Jon Kokott
Link to comment

Definitions:

Master/slave - NI's template implementation.

Incorrect. This only works if you have ONE dequeue place. If you were to try this with multiple "slave" loops it would immediately break down. A queue (even size one) will not perform identically to a notifier.

So I'll ask my original question again:

Does anyone use the master/slave template pattern? You MUST use a notifier as the transport. You MUST have atleast 2 slave loops. You must RELY on lossy transmission (ie its a better solution than a lossless transport.) If that's not the case I won't be satisfied as it is just a different way to solve a problem that could be done using a different, better, pattern.

~Jon

I have used that exact template to terminate parallel loops. In the past -- before Event Structures (and some other tricks...) that was the only way to guarantee shut down of parallel loops that received inputs from queues as well. It's a solution that goes WAY BACK to LV5 or so...

  • Like 1
Link to comment
I don't think lossy vs. lossless is one of the defining characteristics of slaves or consumers. I suspect the template uses notifiers simply because they allow easy 1-to-many communication. I use queues instead because most of the time I want lossless master/slave communication.
I don't think that's true. Note that although the template today uses primitives that I wrote, older versions of the template existed before I started at NI, so I can't be certain of the intent -- I don't even know where they originated -- but to me, the master/slave pattern is useful because the master may change its mind about the next direction. The slave carries out some order. In the interim, the master may change the next order several times. This occurs during navigation type operations, where additional data is coming in all the time about road hazards, so the information sent to the slave is the master's "best guess for next action", which may be changed frequently. It needs to be sent and updated continuously because the master has no idea when the slave is going to get around to asking for the next instruction. Day trading applications use this pattern, with a master system evaluating the market and multiple slaves responding to "buy this" or "sell that" orders, orders that might be redacted before they are actually acted on.
Now I thought I understood the differences between queues and notifiers, but maybe there's something I'm missing. As far as I know these two master/slave implementations are functionally equivalent. Is there an edge condition I'm not aware of that makes them behave differently?

You're missing something bit: Notifiers have an implicit message ID system. The "Preview Queue Element" node will copy the front element of the queue (assuming the queue is not empty) every time the loop repeats. The "Wait For Notification" node will copy the element of the Notifier ONLY if this particular Wait node has not seen this particular notification before. That means that the Notifiers are not only a broadcast mechanism that wakes everyone up when a notification is sent... they are a mechanism that guarantees that each listener is woken up at most one time for that notification.

This is covered in the documentation of Notifiers and various other places on the 'net if you want more details.

Short version: You cannot effectively build a Notifier out of the Queue primitives without introducing some reentrant wrapper VIs that store state so that each Wait call can remember what message ID it saw last. Even more, you cannot build "Wait For Multiple Notifiers" at all without turning to some sort of polling mechanism inside those reentrant VIs or reaching for the very low level Occurrences and using the less-than-intuitive canonical correct usage pattern for Occurrences that I posted last week.

[Later] I went ahead and built the "wait for multiple queues" example because it was such a good example of the purpose of Occurrences. You can see it here.

  • Like 1
Link to comment

I have used that exact template to terminate parallel loops. In the past -- before Event Structures (and some other tricks...) that was the only way to guarantee shut down of parallel loops that received inputs from queues as well. It's a solution that goes WAY BACK to LV5 or so...

Not really the modern NI template for M/S, but completely reasonable. I don't go back to LV5 so I'll presume that releasing the queue did not interrupt the dequeue/preview in those days with an error.

I've done this, and I hate dislike it. It effectively takes an event based processing node and turns it into a polling mechanism. Why have two asynchronous operators in one loop? One of them is necessarily polled to keep the loop going (either by the master to notify "run" or timed out to indicate "don't stop.) This is debatable, but I've taken to releasing the queue to destroy loops. I feel like it offers a more efficient operation. I usually toss a comment in the loop that the error is the exit condition.

Link to comment

Not really the modern NI template for M/S, but completely reasonable. I don't go back to LV5 so I'll presume that releasing the queue did not interrupt the dequeue/preview in those days with an error.

I've done this, and I hate dislike it. It effectively takes an event based processing node and turns it into a polling mechanism. Why have two asynchronous operators in one loop? One of them is necessarily polled to keep the loop going (either by the master to notify "run" or timed out to indicate "don't stop.) This is debatable, but I've taken to releasing the queue to destroy loops. I feel like it offers a more efficient operation. I usually toss a comment in the loop that the error is the exit condition.

Sorry I did the quote then somehow didn't quite add the comment.....maybe somebody can clean it up. I can't seem to find the edit option.

In any event, you have the history reversed -- and it IS about history. The point is that was the ONLY way to do it way back when (at least AFAIK) and so it remains in some legacy code but another reason is that one just might want to trap "error" -- even if it's because of releasing the queue -- and not exit all of the parallel loops but perform some other kind of "recovery" operation.

Link to comment

another reason is that one just might want to trap "error" -- even if it's because of releasing the queue -- and not exit all of the parallel loops but perform some other kind of "recovery" operation.

Trap the error on a released notifier? I don't follow. What other error conditions would be presented from a notifier? Unless you are shifting the link to the outside world (i.e. notifier/queue) and somehow use another reference to get a new "com link" I'm not sure that would be practical. Since all of your objects are "byval" you couldn't even do this.

In any event, you have the history reversed -- and it IS about history.

I'm not following you on this.

~Jon

Link to comment

but to me, the master/slave pattern is useful because the master may change its mind about the next direction. The slave carries out some order. In the interim, the master may change the next order several times.

Are you referring to Labview's master/slave template or are these characteristics you consider inherent to the general pattern? I confess it seems like an odd (and fairly restrictive) way to define a concept as ubiquitous as master/slave. I can see how it would be one particular variation on the theme, but as an overall definition...?

You're missing something bit: Notifiers have an implicit message ID system. The "Preview Queue Element" node will copy the front element of the queue (assuming the queue is not empty) every time the loop repeats. The "Wait For Notification" node will copy the element of the Notifier ONLY if this particular Wait node has not seen this particular notification before. That means that the Notifiers are not only a broadcast mechanism that wakes everyone up when a notification is sent... they are a mechanism that guarantees that each listener is woken up at most one time for that notification.

The sample code I posted doesn't preview the queue--each listener has a dedicated queue and when they receive a message they dequeue and process it like every other message. The two implementations I posted are effectively the same, aren't they?

----------------------------

Later...

Some of the reading I've been doing indicates slaves are identical instances of some functionality dynamically created at runtime. It also implies slaves are frequently created and destroyed as a matter of course in many applications. If that's accurate then that's the part I was missing. Comments?

Link to comment

The sample code I posted doesn't preview the queue--each listener has a dedicated queue and when they receive a message they dequeue and process it like every other message. The two implementations I posted are effectively the same, aren't they?

You can replicate notifier behavior using the dequeue/SEQ if you only need one "slave" loop. The difference is that that pattern doesn't scale to multiple "slave" loops, you are scaling it by creating multiple queue references, vrs with a notifier you only need one reference. There is one other subtle difference between notifiers and an SEQ implementation: With a notifier you can always retrieve the last notification using the "get notifier status." It will immediately return the last notification value. If you were using an SEQ you would have to store the last notification somewhere else as once the dequeue is performed, its pretty much gone.

The idea with notifier "slave" loops is that they all respond to the same command, but the execution time of each loop is independent, and only the latest command is important.

~Jon

Link to comment

The difference is that that pattern doesn't scale to multiple "slave" loops, you are scaling it by creating multiple queue references, vrs with a notifier you only need one reference.

I understand that, but it's largely irrelevant unless you're spawning slaves at runtime. And even then I don't think it's accurate to say queues don't scale... it's just a little more work to make them scale.

With a notifier you can always retrieve the last notification using the "get notifier status."

I hadn't thought of that. (Though off the top of my head I can't think of a reason why I'd need that. My slaves don't need reminders to keep them on task.)

The idea with notifier "slave" loops is that they all respond to the same command, but the execution time of each loop is independent, and only the latest command is important.

I get the idea of overriding previous instructions to the slave. The notifier is easier to use in that situation if your master is sending messages to the slave faster than the slave can process them. Maybe it's just the way I code or the types of problems I'm solving, but that characteristic of notifiers is irrelevant in my messaging systems. Messages don't stack up on the queue so any 'new decisions' are applied equally well by queues as they are by notifiers.

Here's the issue I have with notifier-based slaves in a nutshell:

Notifiers broadcast the exact same message and data to all listeners. Why would I want multiple instances of a process doing exactly the same thing to the same set of data? That's just wasting cpu cycles. They're all going to return the same results. With a notifier I can't split a set of data and sent part to each slave. I can't send unique routing parameters to each slave in a navigation system. What benefit do I get by having a navigation system calculate a path to a destination four times in parallel instead of just once? It's a computer--the slave threads are doing calculations not offering an opinion. If each instance of the slave has a unique input queue then the master has far more flexibility in how to distribute the workload.

I actually find very little need for notifiers in general. I'm not sure I've ever used them specifically for their one-to-many characteristics. I do use them as a data pipe when a data source is acquiring data faster than the consumer needs it, but that decision is based on their relative performance characteristics, not on the need to propogate data to multiple destinations.

Maybe I'm just lacking the imagination needed to get it...

Link to comment

Why would I want multiple instances of a process doing exactly the same thing to the same set of data? That's just wasting cpu cycles.

The idea is you don't do the same processing in each slave. In the navigation system you may have several unrelated processes that act on the same command "get directions home." One loop might search for the most direct route, another loop might try to find a route that results in the highest average speed. The results are then collected and based on the desired parameters (most direct route, least amount of time in car, avoiding hiways, etc it uses the correct route)

Would the actual implementation actually use this kind of a transport layer? That I'm not so sure of. The actual implementation is more complex than that, so who knows if what you'd end up with would look anything like the original description of how the system is "supposed" to work.

~Jon

  • Like 1
Link to comment

The idea is you don't do the same processing in each slave. In the navigation system you may have several unrelated processes that act on the same command "get directions home." One loop might search for the most direct route, another loop might try to find a route that results in the highest average speed. The results are then collected and based on the desired parameters (most direct route, least amount of time in car, avoiding hiways, etc it uses the correct route)

Hrm... so instead of sending the search parameters (most direct, highest average speed, etc) to the slave the parameters are hard coded into the slave? I guess that could be useful in certain situations... each instance will have a smaller memory footprint than if you coded a single slave that can handle different parameters. Overall it still seems like a pretty narrow use case.

(And "master/slave" are still the best terms I can think of for describing the characteristics of the loops I create, though I'll try to take more care to clarify that it's *my* definition.)

Link to comment
  • 2 years later...

Amazingly, I have thought about this question for a long time. However, I thought it's one of those things that just flash into my brain and really don't have any relevance. Surprisingly, the thought flashed in again today and I decided to search on LAVA...that's just the beauty of having many advanced LabVIEW users come together. I got a deep insight into what I thought was just a "flash and irrelevant" thought.

Thanks for all the responses, they've helped a great deal!

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.