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]) ; } ; }
Date of last update: November 16, 1994.