Re: [Boost-users] Does anybody interested in active object
To Dean Michael Berris: Thank you for your patience and detailed answers.
Nope, sorry, s11n_example::connection is not an active object. What it actually is, is an execution context -- or sometimes people call it a continuation, a generator, or a state machine. Because you're using the execution context relevant with a particular connection, it doesn't imply that the operations are performed on a different thread.
Ok. May be I'm wrong trying to name it "active object". "Continuation", "state machine", "execution context" - this is very close to what I had in mind. Therefore, I conclude that we understand each other (despite some language barriers with my hand). I thought all of "Continuation/state machine/execution context" was related to the pattern of "active object", hmm...
If you have unlimited number of cores and no limits on the number of threads you can spawn for each active object you create, then yes.
I think the main feature of "Continuation/state machine/execution context" is not saving threads, but organizing flexible and comfortable way of communication between state machines (yes - it is tasks and queues, but wrapped in async method call with callback). That is what I try to implement by means of Boost.Asio. It was chosen "historically" (started from network programming), but for the now times I see it is the best choice :)
Otherwise, you can stick with continuations, asynchronous completion handlers, and just plain data parallelism.
At the "samples" I develop (thanks to Boost.Asio examples - the are the first) the main goals were: 1) share execution context (thread pool) between different "state machines". 2) (make possible to) divide execution contexts of different "state machines" and application layers when it is needed (session and session_manager - not layers but are close to them). 3) develop a simple way of implementing async method call (for the "state machines") with required callback. (with required usage of "custom memory allocation" which can make usage of async methods as cheap as possible) Regards, Marat Abrarov.
Message: 3 Date: Mon, 8 Nov 2010 10:29:02 +0800 From: Dean Michael Berris
To: boost-users@lists.boost.org Subject: Re: [Boost-users] Does anybody interested in active object Message-ID: Content-Type: text/plain; charset=UTF-8 On Mon, Nov 8, 2010 at 7:11 AM, Marat Abrarov
wrote: To Dean Michael Berris:
You'd be better of doing data parallelism with Boost.MPI
No. Because I'm speaking about thread-based parallelism (with shared memory access). Processed-based parallelism and IPC supplement but don't replace thread-based parallelism in general.
Well, which is why I said 'data parallelism'. This implies a whole different kind of programming, which doesn't typically include threads and synchronization. ;)
and/or Boost.Asio's io_service run on multiple threads, and having independent tasks multiplexed across a number of processors (either through processes or threads)
Tie this tasks with the data they share and You will have an active object.
Nope, sorry, that's not an active object. An Active Object requires that you have a at least one thread of execution dedicated to the active object, and that you have a synchronous interface to that object.
If the tasks don't have shared data - they don't need an active object.
Active Objects really have nothing to do with shared data. It's a design pattern that prescribes a way of isolating the synchronization and serialization of operations applied to an object that is assumed to be shared across multiple threads of execution. The way the active object does it is by lining up the operations to be performed asynchronously and perform those in a different thread of execution.
You can implement an active object without involving shared data -- for example you want to model a processor, or a math kernel, which does all its work on a single thread because maybe you have a library underneath that is not thread-safe. In situations like these it makes sense to have an active object implementation.
I think - active object is built on task-based parallelism but it does not mean that "active object" should replace "task-based parallelism" everywhere.
I think you have it wrong. The active object pattern is a design pattern that doesn't know about task- based parallelism. Task-based parallelism is a different pattern and different paradigm altogether. The active object pattern by itself is just a design pattern, nothing more.
But what you're describing is not the active object pattern. ;)
http://en.wikipedia.org/wiki/Active_object http://en.wikipedia.org/wiki/Active_object_(Symbian_OS) What about ma::echo::server::session class - is it an active object?
I haven't looked at your examples, because I was only commenting on the issue regarding massive parallelism. Maybe when I have enough time I'll look through your examples. ;)
http://www.boost.org/doc/libs/1_44_0/doc/html/boost_asio/example/chat/ chat_c lient.cpp Is chat_client an active object? If "yes" then why? chat_client::write have no completion callback - and this is not enough sometimes (I can say "always", if You don't implementing "fire-and-forget" logic which is rather seldom).
No, this is not an active object. An active object has its own lifetime thread. In this situation the lifetime thread is shared between Boost.Asio's IO subsystem and all the ASIO related handlers.
Also, you don't need a completion callback for a class to model the Active Object pattern. You can create methods that return 'void' and just execute the actions asynchronously in its own lifetime thread.
http://www.boost.org/doc/libs/1_44_0/doc/html/boost_asio/example/seria lizati on/connection.hpp s11n_example::connection is an active object and have completion callback... but it's much simpler then ma::echo::server::session_manager - I try to suggest "samples" that can help to build such a kind of complex active objects.
Nope, sorry, s11n_example::connection is not an active object. What it actually is, is an execution context -- or sometimes people call it a continuation, a generator, or a state machine. Because you're using the execution context relevant with a particular connection, it doesn't imply that the operations are performed on a different thread.
All this is doing is encapsulating callbacks and the protocol implementation into an object. It exposes a means of asynchronously scheduling operations, but it doesn't hide these details from the interface. The goal of the active object pattern is to hide the asynchronous nature of the implementation from the user of the object.
http://www.drdobbs.com/go-parallel/blog/archives/2009/09/parallelism_s ho.htm l
We are surrounded by parallelism even more so that serial activities. Social and biological systems are immersed in parallelism and these systems have worked out the kinks over millions of years. In biological systems, independent units perform simple computations or activities independently and in parallel. and IMPLICATIONS FOR COMPLEX REALTIME SYSTEMS: Complex real-time systems would be a sea of state machine objects interacting with each other. There will be low level state machines which would work together to implement high level state machine behavior.
Here's the place for active object pattern.
If you have unlimited number of cores and no limits on the number of threads you can spawn for each active object you create, then yes. Otherwise, you can stick with continuations, asynchronous completion handlers, and just plain data parallelism.
The place for an active object is where you have to expose a synchronous interface to a resource which underneath performs the actions asynchronously. This is like the half-sync half-async pattern which, once you understand, is what the active object implementation provides.
-- Dean Michael Berris deanberris.com
On Mon, Nov 8, 2010 at 4:53 PM, Marat Abrarov
To Dean Michael Berris:
Thank you for your patience and detailed answers.
No worries. :)
Nope, sorry, s11n_example::connection is not an active object. What it actually is, is an execution context -- or sometimes people call it a continuation, a generator, or a state machine. Because you're using the execution context relevant with a particular connection, it doesn't imply that the operations are performed on a different thread.
Ok. May be I'm wrong trying to name it "active object". "Continuation", "state machine", "execution context" - this is very close to what I had in mind. Therefore, I conclude that we understand each other (despite some language barriers with my hand). I thought all of "Continuation/state machine/execution context" was related to the pattern of "active object", hmm...
Nope, continuation, state machine, and execution context are independent of the active object pattern. There's a PDF somewhere of the web of what the active object pattern is supposed to provide and "look like". It might be good to look into the paper by Dr. Schmidt who introduced the concept a while back: http://www.cs.wustl.edu/~schmidt/PDF/Act-Obj.pdf
If you have unlimited number of cores and no limits on the number of threads you can spawn for each active object you create, then yes.
I think the main feature of "Continuation/state machine/execution context" is not saving threads, but organizing flexible and comfortable way of communication between state machines (yes - it is tasks and queues, but wrapped in async method call with callback).
Well, even there what you really need is a means of encapsulating tasks. If a task has dependencies and you follow the fork-join parallelism model where you spawn additional tasks and pass around a completion callback to do the "join", then that's really what you're looking for. You can look at how TBB does it with its dynamic partitioning to solve data-parallel problems using threads and tasks. This only works though if you have minimal need for synchronization across different tasks. There are many different ways of doing it, and the active object pattern really has nothing to do with massive parallelism.
That is what I try to implement by means of Boost.Asio. It was chosen "historically" (started from network programming), but for the now times I see it is the best choice :)
Hold on there. If you're doing data parallelism internal to an application, just having tasks might not be the best choice. In a massively parallel computation similar to what happens when you deal with graphical systems or with SIMD parallelism, using just Asio+Boost.Thread will not be sufficient (will even be suboptimal). This is why you really ought to look at Boost.MPI, Boost.Interprocess, Boost.Thread, and Boost.Asio for what their strengths are and choose which one you'll need. You choose the right tool for the job. What's really lacking in Boost is well-performing implementations of concurrent queue/hash/<insert data structure here> which we can use in implementing our own producer/consumer queue and other parallel partition-based algorithms. Asio just so happens to have a simple io_service which does the dispatch and queuing internally -- (ab)using that is not the best use of the library. Also, there is already a proposal for C++0x to include a std::thread_pool where you can throw tasks into. I'm not sure if Boost.Thread will implement this or whether there are already other libraries that do something similar, but that really is what you need.
Otherwise, you can stick with continuations, asynchronous completion handlers, and just plain data parallelism.
At the "samples" I develop (thanks to Boost.Asio examples - the are the first) the main goals were: 1) share execution context (thread pool) between different "state machines".
An execution context is not the thread pool. An execution context is usually an object which has state encapsulated which is relevant to the task. If you think about boost::bind(...) creating a function object that has bound parameters, then the function object is said to have its execution context encapsulated. This is how continuations work, and how stackless programming works as well -- you rely on the context that is available/local to the task/function when it is invoked at some point in time.
2) (make possible to) divide execution contexts of different "state machines" and application layers when it is needed (session and session_manager - not layers but are close to them).
You don't need Asio here.
3) develop a simple way of implementing async method call (for the "state machines") with required callback. (with required usage of "custom memory allocation" which can make usage of async methods as cheap as possible)
There is a pattern called the accumulator -- which is technically a function object that maintains its state across invocations. For example, you might have an object that keeps track of the number of times it's been called and every time its function operator overload is called, it returns the previous count. Like this: struct accumulator { accumulator() : count(0u) {} unsigned int operator()() { return count++; } unsigned int count; } In other programming languages that support closures, these would be something you might call a closure. The point here is that all you need to be able to perform asynchronous tasks and actions on different threads is for you to bring the data required for that operation to complete and package it up as a singular piece of computation as a function object. This is how tasks work, this is how Boost.Asio completion handlers are invoked, and this is how task-based programming works. If you discuss the fundamentals of these things instead of trying to go to complex examples to bring the point across, maybe it would be better, just a tip. ;) Also, using more canonical terms would help a lot as well. :D Oh, and please, don't top post. HTH -- Dean Michael Berris deanberris.com
participants (2)
-
Dean Michael Berris
-
Marat Abrarov