|
![]() |
Group communication is a crucial feature for high-performance and Grid
computing. While previous works and libraries proposed such a
characteristic (e.g. MPI, or object-oriented frameworks), the
use of groups imposed specific constraints on programmers, for
instance the use of dedicated interfaces to trigger group
communications.
We aim at a more flexible mechanism. We propose a scheme where, given
a Java class, one can initiate group communications using the
standard public methods of the class together with the classical
dot notation; in that way, group communications remains typed.
In order to ease the use of the group communication, we provide a set
of static methods on the ProActiveGroup
class and a set of methods on the Group
interface.
Here, a short compilation about the syntax and some method used in
the Group Communication API is presented. More informations follow.
// created at once, // with parameters specified in params, // and on the nodes specified in nodes A ag1 = (A) ProActiveGroup.newGroup( "A", params, [nodes]); // A general group communication without result // A request to foo is sent in parallel to all active objects // in the target group (ag1) ag1.foo(...); // A general group communication with a result V vg = ag1.bar(...); // vg is a typed group of "V": operation // below is also a collective operation // triggered on results vg.f1();
Any object that is reifiable has the ability to be included in a
group. Groups are created using the static method
ProActiveGroup.newGroup
.
The common superclass for all the group
members has to be specified, thus giving the group a minimal type.
Let us take a standard Java class:
class A { public A() {} public void foo (...) {...} public V bar (...) {...} ... }
Here are examples of some group creation operations:
// Pre-construction of some parameters: // For constructors: Object[][] params = {{...} , {...} , ... }; // Nodes to identify JVMs to map objects Node[] nodes = { ... , ..., ... }; // Solution 1: // create an empty group of type "A" A ag1 = (A) ProActiveGroup.newGroup("A"); // Solution 2: // a group of type "A" and its members are // created at once, // with parameters specified in params, // and on the nodes specified in nodes A ag2 = (A) ProActiveGroup.newGroup("A", params, nodes); // Solution 3: // a group of type "A" and its members are // created at once, // with parameters specified in params, // and on the nodes directly specified A ag3 = (A) ProActiveGroup.newGroup("A", params[], {rmi://globus1.inria.fr/Node1, rmi://globus2.inria.fr/Node2});
Elements can be included into a typed group only if their class equals or extends the class specified at the group creation. For example, an object of class B (B extending A) can be included to a group of type A. However based on Java typing, only the methods defined in the class A can be invoked on the group.
The typed group representation we have presented corresponds to the functional
view of groups of objects.
In order to provide a dynamic management of groups, a second
and complementary representation of a group has been designed. In
order to manage a group, this second representation must
be used instead.
This second representation, the management representation, follows a more standard pattern
for grouping objects : the Group
interface.
We are careful to have a strong coherence between
both representations of the same group, which implies that
modifications executed through one representation are
immediately reported on the other one.
In order to switch from one representation to the other,
two methods have been defined :
the static method named
ProActiveGroup.getGroup
,
returns the Group form associated to the given group object;
the method getGroupBytype
defined in the Group
interface
does the opposite.
Below is an example of when and how to use each representation of a group:
// definition of one standard Java object // and two active objects A a1 = new A(); A a2 = (A) ProActive.newActive("A", paramsA[], node); B b = (B) ProActive.newActive("B", paramsB[], node); // Note that B extends A // For management purposes, get the representation // as a group given a typed group, created with // code on the left column: Group gA = ProActiveGroup.getGroup(ag1); // Now, add objects to the group: // Note that active and non-active objects // may be mixed in groups gA.add(a1); gA.add(a2); gA.add(b); // The addition of members to a group immediately // reflects on the typed group form, so a method // can be invoked on the typed group and will // reach all its current members ag1.foo(); // the caller of ag1.foo() may not belong to ag1 // A new reference to the typed group // can also be built as follows A ag1new = (A) gA.getGroupByType();
The particularity of our group communication mechanism is that the result of a typed group communication is also a group. The result group is transparently built at invocation time, with a future for each elementary reply. It will be dynamically updated with the incoming results, thus gathering results. Nevertheless, the result group can be immediately used to execute another method call, even if all the results are not available. In that case the wait-by-necessity mechanism implemented by ProActive is used.
// A method call on a group, returning a result V vg = ag1.bar(); // vg is a typed group of "V": operation // below is also a collective operation // triggered on results vg.f1();
As said in the Group creation section, groups whose type is based on final classes
or primitive types cannot be built. So, the construction of a dynamic group
as a result of a group method call is also limited.
Consequently, only methods whose return type is either void or
is a 'reifiable type', in the sense of the Meta Object Protocol of ProActive,
may be called on a group of objects; otherwise, they
will raise an exception at run-time, because the
transparent construction of a group of futures of
non-reifiable types fails.
To take advantage with the asynchronous remote method call model of ProActive,
some new synchronization mechanisms have been added.
Static methods defined in the ProActiveGroup
class enable to execute various forms of synchronisation. For instance:
waitOne
,
waitN
,
waitAll
,
waitTheNth
,
waitAndGet
.
Here is an exemple :
// A method call on a typed group V vg = ag1.bar(); // To wait and capture the first returned // member of vg V v = (V) ProActiveGroup.waitAndGetOne(vg); // To wait all the members of vg are arrived ProActiveGroup.waitAll(vg);
Regarding the parameters of a method call towards a group of
objects, the default behaviour is to broadcast them to
all members. But sometimes, only a specific portion of the parameters,
usually dependent of the rank of the member in the group,
may be really useful for the method execution, and so,
parts of the parameter transmissions are useless.
In other words, in some cases, there is a
need to transmit different parameters to the various members.
A common way to achieve the scattering of a global parameter is
to use the rank of each member of the group, in order to select
the appropriate part that it should get
in order to execute the method. There is
a natural traduction of this idea inside
our group communication mechanism:
the use of a group of objects in order to represent
a parameter of a group method call that must
be scattered to its members.
The default
behaviour regarding parameters passing for method
call on a group, is to pass a deep copy
of the group of type P to all members.
Thus, in order to scatter this group of elements
of type P instead, the programmer must
apply the static method setScatterGroup
of the ProActiveGroup
class to the group.
In order to switch back to the default behaviour, the
static method unsetScatterGroup
is available.
// Broadcast the group gb to all the members // of the group ag1: ag1.foo(gb); // Change the distribution mode of the // parameter group: ProActiveGroup.setScatterGroup(gb); // Scatter the members of gb onto the // members of ag1: ag1.foo(gb);