Tutorial - Shared Memory

From Mesham
Revision as of 15:44, 15 April 2019 by Polas (talk | contribs) (11 revisions imported)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

Tutorial number five - prev :: next

Introduction

In this tutorial we will be looking at using the, default, shared memory model for simple communication involving a single variable. It is important to understand the memory model behind this form of communication and when variables will be subject to communication.

Shared memory model

Mesham follows the Logic Of Global Synchrony (LOGS) model of shared memory. This actually sounds much more formidable than it is in reality and follows a simple number of practical rules. Each variable can be thought of as starting in one state, finishing in another (if and when the code terminates) and throughout the program's life be in a number of intermediate states. We go from one intermediate state to the next when synchronisation is used and this can be thought of as barrier synchronisation.

My first communication

Communication depends on exactly where variables are allocated to which in itself is driven by types.

#include <io>
#include <string>

function void main() {
   var a:Int::allocated[multiple[]];
   a:=1;
   proc 1 {
      a:=99;
   };
   sync a;
   proc 0 {
      print(itostring(a)+"\n");
   };
};

If you compile and run the following code then you will see the output 1 - so lets have a look what exactly is going on here. Variable a is allocated to all processes, all processes set the value to be 1, process one will then change the value to be 99, we do a barrier synchronisation on a and then process zero will display its value. Because a is allocated to all processes (via the multiple type), assignment and access is always local - i.e. in this case, process one modifying the value will have no impact on a held on other processes such as process zero.

So we have seen that variables allocated to all processes always involve local access and assignment, let's do something a bit more interesting - change the multiple[] to be single[on[0]] and recompile and run the code. Now the output is different and it displays 99. That is because if a variable is allocated just to a specific process and another one reads/writes to it, then this will involve remote access to that memory (communication.) Let's experiment further with this, remove a from the sync statement (line 10) and recompile and rerun, the result should be the same, 99 displayed. If we specify a variable with the sync keyword then this will barrier synchronise just on that variable, the sync by itself will barrier synchronise on all variables which require it. Ok then, now comment out the sync keyword entirely and recompile and run the code - see it now displays 1 again? This is because we can only guarantee that a value has been written into some remote memory after barrier synchronisation has occurred.

We have seen that if a variable is allocated to all processes then read/write will always be a local operation but if a variable is allocated just to a single process then read/write will be a remote operation on every other process.

Further communication

#include <io>
#include <string>

function void main() {
   var a:Int::allocated[single[on[0]]];
   var b:Int::allocated[multiple[]];
   proc 0 {
      a:=1;
   };
   sync ;
   proc 1 {
      b:=a;
      sync a;
      print(itostring(b)+"\n");
   };
};

The code snippet above is similar to the first one but with some important differences. We are declaring two variables; the first, a, is held on process zero only whereas the second, b, is allocated to all processes. Process zero then alone (via the proc statement will modify a locally (as it is held there). We then synchronise all processes to ensure process zero has updated a and then process one will obtain the value of a from process zero and pop this into its own b, it then completes all operations involving variable a and displays its value of b. Stepping back a moment, what we are basically doing here is getting some remote data and copying this into a local variable, the result is that the value held by process zero in a will be retrieved into b on process one. If you remove the sync statement on line 10 then you might see that instead of displaying the value 1, 0 is displayed (the default Int initialisation value.) This is because synchronisation must occur to ensure process zero has update a before process one reads from it, equally the last synchronisation statement completes RMA and if you remove this then likely the value in b will not have updated.

#include <io>
#include <string>

function void main() {
  var a:Int::allocated[multiple[commgroup[0,2]]];
  var b:Int::allocated[single[on[1]]];
  group 0, 2 {
     a:=2;
     b:=a;
  };
  sync;   
  proc 1 {
     print(itostring(b)+"\n");
  };
};

The above illustrates a communication group, as this has to be provided with Multiple the variable a is private to each process that it is allocated on. Here processes zero and two update their own (local) version of a and then remotely write to variable b held on process one, both processes will send values over but as these are the same then there is no conflict. synchronisation is used to complete the RMA and ensure process one awaits updates to its b which it then displays.

Single to single

If we have two variables which are allocated to single processes then any assignment involving these will either result in local or remote access depending on whether they are on the same process or not.

#include <io>
#include <string>

var processOneAllocation:=0;
var processTwoAllocation:=0;

function void main() {
   var a:Int::allocated[single[on[processOneAllocation]]];
   var b:Int::allocated[single[on[processTwoAllocation]]];
   proc processTwoAllocation {
      b:=23;
      a:=b;
   };
   //sync;
   group processOneAllocation {
      print(itostring(a)+"\n");
   };
};

In the example above we are allocating variables a and b both on process zero, we are then performing an assignment a:=b at line 12 which, because the variables are on the same process is local and occurs immediately. Now, change processOneAllocation to be equal to 1 and uncomment the sync keyword at line 14 and recompile and run. See the same value - but now process 0 is writing the value of b into the remote memory of a and if you comment out the sync keyword then a value of 0 will be reported. The values of processOneAllocation and processTwoAllocation can be anything - if they are the same here then it is local and if not then remote.

Limits of communication

Currently all variables declared multiple (including communication groups) should be considered private, it is only variables declared single which can be accessed by another process.