Lecture 22
                              11/29/1994


The Linda Programming Model
---------------------------
Linda is not a complete programming language. Rather it is a set of
objects and operations on those objects that are intended to be
"injected" into an existing language, thus producing a new language
intended for distributed/parallel programming. The following is a
simple language-independent presentation of Linda. In this lecture will
be looking at a C-flavoured Linda (C-Linda). 

Linda Objects:
-------------
Linda is based on two fundamental objects: tuples and tuple spaces.

A tuple is a collection of fields. Each field has fixed types
associated with them, which are drawn from the underlying language. A
field can be "formal" or "actual". Formals are space holders with
types but no values (very similar to the formals in a function
definition). An "actual" field has a value of its particular
type. Formal fields are created by adding a "?" in front of a variable
of the language. The definition of Linda places no constraints on the
types of the underlying language. Also, tuples may have any number of
fields.

Tuples live in tuple space, which is simply a collection of
tuples. The tuple space is a bag and not a set, so it may contain
several copies of the same tuple. The tuple space is the fundamental
medium of communication in Linda. Linda processes communicate through
the tuple space -- the sender interacts with the tuple space and the
receiver interacts with the tuple space (not necessary in that order!)

Linda Operators:
---------------
The OUT operator inserts a tuple into the tuple space. The IN operator
extracts a tuple from the tuple space. It finds a tuple that "matches"
its arguments. The RD operator os similar to IN, except that it does
not remove the matched tuple from the tuple space -- it leaves it
unchanged in the tuple space. 

Examples:
--------
out(1,f,i) ;
out(1,?f,i) ;

in(1,1.5,2) ;
in(1,?f,2) ;

rd(1,1.5,2) ;
rd(1,?f,2) ;

Tuple matching
--------------
The IN and RD operators are defined in terms of matching. The tuple
defined by the fields in an IN or RD is a template. A template M and a
tuple T match if:

1. M and T have the same number of fields ;
2. Corresponding fields have the same types ;
3. Each pair of corresponding fields Fm and Ft (in M and T respectively) 
   match. Two fields match only if: 
   - If both Fm and Ft are actuals with "equal" values
   - If Fm is a formal and Ft an actual
   - If Ft is a formal and Fm an actual

If a template matches a tuple then any formals in the template are
assigned values from the the tuple.

If no matching tuple can be found for a template of a RD or IN, then
these operations block and the process awaits for a tuple to appear. This
could be used for synchronization. Two non-blocking "predicates" that act
like IN and RD are INP and RDP. These operations attempt to locate a
matching tuple (as described above) and return 0 if they fail and 1 if
they succeed. If they succeed then any formals in the template of the INP
or RDP are assigned values from the tuple that matched the template.

The OUT operator actually injects the tuple into the tuple space after
the evaluation of all the fields therein. For example

out("PI =", compute_PI()) ;

will result in calling the function "compute_PI()" first and then
depositing the tuple with the value returned by that function in the
second field. It is equivalent to 

p = compute_PI() ;
out("PI =", p) ;

The last operator in Linda, EVAL, allows the injection of a tuple that is
not completely evaluated in the tuple space (this is like inserting a
tuple with a promise to provide the value "later"). For example:

eval("PI =", compute_PI()) ;

will result in the insertion of a tuple, whose second field is "live" (a
future). 

The EVAL operator could be used to fork off processes, who themselves
will do the work in parallel. For example:

for (i=0; i<100; i++)
  eval("function", i, f(i)) ;

will result in the "forking off" of 100 live tuples, each with the value
of the function for a different argument.

Alternately, the EVAL operator could be used to fork off processes, who
themselves will be able to "input" or "output" tuples. For example in the
following code a producer/consumer setup is created to do the evaluation
of the function f() for various arguments.


main()
{
...
  eval("function", f()) ;
  eval("function", f()) ;
  eval("function", f()) ;
  ...
  for (i=0; i<100; i++)
    out("data", i) ;
...
}

f()
{
...
  in("data", ?x) ;
  y = g(x) ;
  out("function", x, y) ;
...
}


Some Examples
-------------
We would like to compute the dot product of two large vectors A and B
that reside in a shared memory. We do so by forking off parallel
processes that do the computation on partial components of A and B and
then collect the results. Let's assume that the total number of
processors available is m and that the dimension of A, B is n. The
following code will do it!


double dot_product(A, B, ID)
  double A[n], B[n]; 
  int ID ;
{
  ...
  for (i=0 ; i < m ; i++)
    eval("partial-dot", ID, partial_dot_product(A,B,i*(n/m),(n/m))) ;
  ...

  sum = 0 ;
  for (i=0 ; i < m ; i++) {
    in("partial-dot", ID, ?p) ;
    sum += p ;
  }
  return(sum) ;
}


double partial_dot_product(A,B,start,length) ;
  double A[n], B[n] ; 
  int start, length ;
{
  ...
  sum = 0 ;
  for (i=start; i < length; i++)
    sum += A[i]*B[i] ;
  return(sum) ;
}

Notice that the above process can be repeated (for example to assign 
different rows and columns to workers in order to perform a matrix
multiplication). For example, let X and Y be 2 nxn dimensional matrices,
the following code will compute the product Z = X*transpose(Y).


matrix_multiply(X,Y,Z)
  double X[n][n], Y[n][n], Z[n][n] ;
{
  ...
  for (i=0 ; i < n ; i++)
    for (j=0 ; i < n ; j++) {
      ID = i*n + j ;
      eval("dot", ID, dot_product(&X[i], &X[j], ID)) ;
      } ;

  for (i=0 ; i < n ; i++)
    for (j=0 ; j < n ; j++) {
      ID = i*n + j ;
      in("dot", ID, Z[i][j]) ;
      } ;
}


This document has been prepared by Professor Azer Bestavros <best@cs.bu.edu> as the WWW Home Page for CS-551, which is part of the NSF-funded undergraduate curriculum on parallel computing at BU.

Date of last update: November 16, 1994.