Initial addition of jacobi example using Cell.
authorDavid Kunzman <kunzman2@illinois.edu>
Fri, 24 Mar 2006 00:21:31 +0000 (00:21 +0000)
committerDavid Kunzman <kunzman2@illinois.edu>
Fri, 24 Mar 2006 00:21:31 +0000 (00:21 +0000)
examples/charm++/cell/jacobi/Makefile [new file with mode: 0644]
examples/charm++/cell/jacobi/jacobi.C [new file with mode: 0644]
examples/charm++/cell/jacobi/jacobi.ci [new file with mode: 0644]
examples/charm++/cell/jacobi/jacobi.h [new file with mode: 0644]
examples/charm++/cell/jacobi/jacobi_shared.cpp [new file with mode: 0644]
examples/charm++/cell/jacobi/jacobi_shared.h [new file with mode: 0644]
examples/charm++/cell/jacobi/main.C [new file with mode: 0644]
examples/charm++/cell/jacobi/main.ci [new file with mode: 0644]
examples/charm++/cell/jacobi/main.h [new file with mode: 0644]

diff --git a/examples/charm++/cell/jacobi/Makefile b/examples/charm++/cell/jacobi/Makefile
new file mode 100644 (file)
index 0000000..3e7a7f7
--- /dev/null
@@ -0,0 +1,48 @@
+CHARM_BASE_DIR = ../../../..
+CHARM_BIN_DIR = $(CHARM_BASE_DIR)/bin
+CHARMC = $(CHARM_BIN_DIR)/charmc $(OPTS)
+
+GENERAL_DEPENDS = Makefile
+
+# NOTE : Mainfile.cell will set some variables including : CELL_SDK_DIR, SPU_CXX, PPU_EMBEDSPU, etc.
+include $(CHARM_BASE_DIR)/tmp/Makefile.cell
+
+
+default : all
+all : jacobi
+
+
+#///////////////////////////////////////////////////////////////////////////////
+#// General Charm++ Stuff
+
+# Rule to combine all the object files
+jacobi : main.o jacobi.o jacobi_shared.o
+       $(CHARMC) -language charm++ -o jacobi main.o jacobi.o jacobi_shared.o
+
+# Rule to compile the Main class
+main.o : main.C main.h jacobi_shared.h main.decl.h jacobi.decl.h $(GENERAL_DEPENDS)
+       $(CHARMC) -c -o main.o main.C
+
+# Rule to compile the Jacobi class
+jacobi.o : jacobi.C jacobi.h jacobi_shared.h jacobi.decl.h main.decl.h $(GENERAL_DEPENDS)
+       $(CHARMC) -c -o jacobi.o jacobi.C jacobi_shared.cpp
+
+# General rule for creating decl and def files
+%.decl.h : %.ci $(GENERAL_DEPENDS)
+       $(CHARMC) $*.ci
+
+
+#///////////////////////////////////////////////////////////////////////////////
+#// Cell Specific Stuff
+
+# Rule to create the embeded spu runtime that includes the user's shared code
+jacobi_shared.o : jacobi_shared.cpp jacobi_shared.h $(GENERAL_DEPENDS)
+       $(SPU_CXX) -I$(CELL_SDK_DIR)/sysroot/usr/spu/include -L$(CHARM_BASE_DIR)/lib -o jacobi_shared jacobi_shared.cpp -lcellspu
+       $(PPU_EMBEDSPU) spert_main jacobi_shared jacobi_shared.o
+
+
+#///////////////////////////////////////////////////////////////////////////////
+#// Clean-up Stuff
+
+clean :
+       $(RM) *.decl.h *.def.h *.o jacobi_shared jacobi charmrun
diff --git a/examples/charm++/cell/jacobi/jacobi.C b/examples/charm++/cell/jacobi/jacobi.C
new file mode 100644 (file)
index 0000000..17f6dba
--- /dev/null
@@ -0,0 +1,162 @@
+#include <stdio.h>
+
+#include "jacobi.decl.h"
+#include "jacobi.h"
+#include "jacobi_shared.h"
+
+#include "main.decl.h"
+
+
+extern /* readonly */ CProxy_Main mainProxy;
+
+
+Jacobi::Jacobi() {
+
+  // Init member variables
+  ghostCount = 0;
+  ghostCountNeeded = 4;
+  int chareX = GET_CHARE_X(thisIndex);
+  int chareY = GET_CHARE_Y(thisIndex);
+  if (chareX == 0) ghostCountNeeded--;
+  if (chareY == 0) ghostCountNeeded--;
+  if (chareX == NUM_CHARES - 1) ghostCountNeeded--;
+  if (chareY == NUM_CHARES - 1) ghostCountNeeded--;
+
+  // Allocate memory for the buffers
+  // NOTE: Each buffer will have enough room for all the data (local data + ghost data from bordering chares)
+  matrix = (float*)malloc_aligned(sizeof(float) * DATA_BUFFER_SIZE, 16);
+  matrixTmp = (float*)malloc_aligned(sizeof(float) * DATA_BUFFER_SIZE, 16);
+
+  // Initialize the data
+  memset(matrix, 0, sizeof(float) * DATA_BUFFER_SIZE);
+  memset(matrixTmp, 0, sizeof(float) * DATA_BUFFER_SIZE);
+
+  // If this is the first element, set it's matrix[DATA_OFFSET] to 1.0f (this is the only fixed point)
+  if (thisIndex == 0) {
+    matrixTmp[DATA_OFFSET] = matrix[DATA_OFFSET] = 1.0f;
+    matrixTmp[DATA_BUFFER_COLS - 1] = matrix[DATA_BUFFER_COLS - 1] = 1.0f;  // Flag the first element's matrices
+  }
+}
+
+Jacobi::Jacobi(CkMigrateMessage *msg) {
+}
+
+Jacobi::~Jacobi() {
+  if (matrix != NULL) { free_aligned(matrix); matrix = NULL; }
+  if (matrixTmp != NULL) { free_aligned(matrixTmp); matrixTmp = NULL; }
+}
+
+void Jacobi::startIteration() {
+
+  // Send ghost data to the neighbors
+  int chareX = GET_CHARE_X(thisIndex);
+  int chareY = GET_CHARE_Y(thisIndex);
+
+  // DEBUG
+  static int debugFlag = 0;
+  if (thisIndex == 0 && debugFlag == 0) {
+    CkPrintf("Jacobi[0]::startIteration() - Called...\n");
+    debugFlag = 1;
+  }
+
+  // Send to the north
+  if (chareY > 0) {
+    thisProxy[GET_CHARE_I(chareX, chareY-1)].southData(NUM_COLS, matrix + DATA_NORTH_DATA_OFFSET);
+  }
+
+  // Send to the south
+  if (chareY < (NUM_CHARES - 1)) {
+    thisProxy[GET_CHARE_I(chareX, chareY+1)].northData(NUM_COLS, matrix + DATA_SOUTH_DATA_OFFSET);
+  }
+
+  // Send to the west
+  if (chareX > 0) {
+    float buf[NUM_ROWS];
+    for (int i = 0; i < NUM_ROWS; i++)
+      buf[i] = matrix[DATA_BUFFER_COLS * i + DATA_WEST_DATA_OFFSET];
+    thisProxy[GET_CHARE_I(chareX - 1, chareY)].eastData(NUM_ROWS, buf);
+  }
+
+  // Send to the east
+  if (chareX < (NUM_CHARES - 1)) {
+    float buf[NUM_ROWS];
+    for (int i = 0; i < NUM_ROWS; i++)
+      buf[i] = matrix[DATA_BUFFER_COLS * i + DATA_EAST_DATA_OFFSET];
+    thisProxy[GET_CHARE_I(chareX + 1, chareY)].westData(NUM_ROWS, buf);
+  }
+}
+
+void Jacobi::northData(int size, float* ghostData) {
+  memcpy(matrix + DATA_NORTH_BUFFER_OFFSET, ghostData, NUM_COLS * sizeof(float));
+  attemptCalculation();
+}
+
+void Jacobi::southData(int size, float* ghostData) {
+  memcpy(matrix + DATA_SOUTH_BUFFER_OFFSET, ghostData, NUM_COLS * sizeof(float));
+  attemptCalculation();
+}
+
+void Jacobi::eastData(int size, float* ghostData) {
+  for (int i = 0; i < NUM_ROWS; i++)
+    matrix[DATA_BUFFER_COLS * i + DATA_EAST_BUFFER_OFFSET] = ghostData[i];
+  attemptCalculation();
+}
+
+void Jacobi::westData(int size, float* ghostData) {
+  for (int i = 0; i < NUM_ROWS; i++)
+    matrix[DATA_BUFFER_COLS * i + DATA_WEST_BUFFER_OFFSET] = ghostData[i];
+  attemptCalculation();
+}
+
+void Jacobi::attemptCalculation() {
+
+  ghostCount++;
+  if (ghostCount >= ghostCountNeeded) {
+    // Reset ghostCount for the next iteration
+    // NOTE: No two iterations can overlap because of the reduction
+    ghostCount = 0;
+
+    // Send a message so the threaded doCalculation() entry method will be called
+    thisProxy[thisIndex].doCalculation();
+  }
+}
+
+void Jacobi::doCalculation() {
+
+  // Send the work request to the Offload API
+  WRHandle wrHandle = sendWorkRequest(FUNC_DoCalculation,
+                                      NULL, 0,                                      // readWrite data
+                                      matrix, sizeof(float) * DATA_BUFFER_SIZE,     // readOnly data
+                                      matrixTmp, sizeof(float) * DATA_BUFFER_SIZE,  // writeOnly data
+                                      CthSelf()
+                                     );
+  if (wrHandle == INVALID_WRHandle)
+    CkPrintf("Jacobi[%d]::doCalculation() - ERROR - sendWorkRequest() returned INVALID_WRHandle\n", thisIndex);
+  else
+    CthSuspend();
+
+  // Get the maxError calculated by the work request and contribute it the reduction for this overall iteration
+  contribute(sizeof(float), matrixTmp, CkReduction::max_float);
+
+
+  // Display the matrix
+  #if DISPLAY_MATRIX != 0
+    printf("matrix[%d] = {\n", thisIndex);
+    for (int y = 0; y < DATA_BUFFER_ROWS; y++) {
+      printf("  ");
+      for (int x = 0; x < DATA_BUFFER_COLS; x++) {
+        printf(" %f", matrix[GET_DATA_I(x,y)]);
+      }
+      printf("\n");
+    }
+    printf("}\n");
+  #endif
+
+  // Swap the matrix and matrixTmp pointers
+  float *tmp = matrix;
+  matrix = matrixTmp;
+  matrixTmp = tmp;
+}
+
+
+#include "jacobi.def.h"
diff --git a/examples/charm++/cell/jacobi/jacobi.ci b/examples/charm++/cell/jacobi/jacobi.ci
new file mode 100644 (file)
index 0000000..f5c77be
--- /dev/null
@@ -0,0 +1,17 @@
+module jacobi {
+
+  array [1D] Jacobi {
+
+    entry Jacobi(void);
+
+    entry void startIteration(void);
+
+    entry void northData(int size, float ghostData[size]);
+    entry void southData(int size, float ghostData[size]);
+    entry void eastData(int size, float ghostData[size]);
+    entry void westData(int size, float ghostData[size]);
+
+    entry [threaded] void doCalculation();
+  };
+
+};
diff --git a/examples/charm++/cell/jacobi/jacobi.h b/examples/charm++/cell/jacobi/jacobi.h
new file mode 100644 (file)
index 0000000..053f119
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef __JACOBI_H__
+#define __JACOBI_H__
+
+#include "jacobi.decl.h"
+#include "jacobi_shared.h"
+
+
+class Jacobi : public CBase_Jacobi {
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  //  Member Variables
+  private:
+    float* matrix;     // Pointer to the first buffer (the read buffer per iteration)
+    float* matrixTmp;  // Pointer to the second buffer (the write buffer per iteration)
+    int ghostCount;
+    int ghostCountNeeded;
+
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  // Constructor(s) / Destructor
+  public:
+    Jacobi();
+    Jacobi(CkMigrateMessage *msg);
+    ~Jacobi();
+
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  // Member Functions
+  public:
+    void startIteration();
+
+    void northData(int size, float* ghostData);
+    void southData(int size, float* ghostData);
+    void eastData(int size, float* ghostData);
+    void westData(int size, float* ghostData);
+
+    void attemptCalculation();
+    void doCalculation();
+};
+
+
+#endif //__JACOBI_H__
diff --git a/examples/charm++/cell/jacobi/jacobi_shared.cpp b/examples/charm++/cell/jacobi/jacobi_shared.cpp
new file mode 100644 (file)
index 0000000..e0d027d
--- /dev/null
@@ -0,0 +1,64 @@
+#include <stdio.h>
+
+#include "jacobi_shared.h"
+
+
+void funcLookup(int funcIndex,
+                void* readWritePtr, int readWriteLen,
+                void* readOnlyPtr, int readOnlyLen,
+                void* writeOnlyPtr, int writeOnlyLen
+               ) {
+
+  switch (funcIndex) {
+
+    case FUNC_DoCalculation: doCalculation((float*)writeOnlyPtr, (float*)readOnlyPtr); break;
+
+    default:
+      printf("!!! WARNING !!! :: SPE Received Invalid funcIndex (%d)... Ignoring...\n", funcIndex);
+      break;
+  }
+}
+
+
+void doCalculation(volatile float* matrixTmp, volatile float* matrix) {
+
+  float maxError = 0.0f;
+
+  // Update matrixTmp with new values
+  register int isNorthwestern = (matrix[DATA_BUFFER_COLS - 1] == 1.0f);
+  register int startX = ((isNorthwestern) ? (2) : (1));
+  for (int y = 1; y < (DATA_BUFFER_ROWS - 1); y++) {
+    for (int x = startX; x < (DATA_BUFFER_COLS - 1); x++) {
+
+      matrixTmp[GET_DATA_I(x,y)] = (matrix[GET_DATA_I(x    , y    )] +
+                                    matrix[GET_DATA_I(x - 1, y    )] +
+                                    matrix[GET_DATA_I(x + 1, y    )] +
+                                    matrix[GET_DATA_I(x    , y - 1)] +
+                                    matrix[GET_DATA_I(x    , y + 1)]
+                                  ) / 5.0f;
+
+      float tmp = matrixTmp[GET_DATA_I(x,y)] - matrix[GET_DATA_I(x,y)];
+      if (tmp < 0.0f) tmp *= -1.0f;
+      if (maxError < tmp) maxError = tmp;
+    }
+    startX = 1;
+  }
+
+  // NOTE : Since the writeOnlyPtr is being used, the data buffer is initially allocated
+  //   on the SPE and is not initialized.  Make sure to write to all the entries so there
+  //   are no "junk" values anywhere in matrixTmp when it is passed back to main memory.
+  //   Though, ignore the areas that will be overwritten when ghost data arrives next
+  //   iteration.  This is a tradeoff... have to do this or use the readWritePtr.  These
+  //   stores to the LS are probably faster than DMAing the matrixTmp buffer down to the LS.  ;)
+  matrixTmp[GET_DATA_I(0, DATA_BUFFER_ROWS - 1)] = 0.0f;                     // unused corner (here for completeness)
+  matrixTmp[GET_DATA_I(DATA_BUFFER_COLS - 1, DATA_BUFFER_ROWS - 1)] = 0.0f;  // unused corner (here for completeness)
+  if (isNorthwestern) {
+    matrixTmp[GET_DATA_I(1, 1)] = 1.0f;  // Hold this single element constant across iterations
+    matrixTmp[GET_DATA_I(DATA_BUFFER_COLS - 1, 0)] = 1.0f;  // northwest flag
+  } else {
+    matrixTmp[GET_DATA_I(DATA_BUFFER_COLS - 1, 0)] = 0.0f;  // northwest flag
+  }
+
+  // Place the maxError in index 0 of matrixTmp
+  matrixTmp[GET_DATA_I(0, 0)] = maxError;
+}
diff --git a/examples/charm++/cell/jacobi/jacobi_shared.h b/examples/charm++/cell/jacobi/jacobi_shared.h
new file mode 100644 (file)
index 0000000..9cf3dee
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef __JACOBI_SHARED_H__
+#define __JACOBI_SHARED_H__
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Defines
+
+// Explination of Data Structure
+// Each of the buffers (matrix and matrixTmp) are decomposed as follows.  A single data
+//   structure is used so that it may easily be moved to and from the SPEs.  There are
+//   two of the data structures, one is read and one is written during each iteration.
+//   After an iteration, they are switched so the one that was read during iteration 'i'
+//   is written to during iteration 'i+1'.
+//
+//        |/__ NUM_COLS  __\|
+//        |\               /|
+//
+//    MMM NNN NNN ... NNN NNN FFF  ____
+//    WWW DDD DDD ... DDD DDD EEE   /\
+//    ... ... ... ... ... ... ...   NUM_ROWS
+//    WWW DDD DDD ... DDD DDD EEE  _\/_
+//    --- SSS SSS ... SSS SSS ---
+//
+// Where:
+//   MMM : Holds the maxError for a sub-matrix per iteration (filled in by doCalculation on SPE)
+//   NNN : Is the nothern ghost data
+//   SSS : Is the southern ghost data
+//   WWW : Is the western ghost data
+//   EEE : Is the eastern ghost data
+//   FFF : Is a flag set for the first element (most north-west) so the single constant value
+//           is not changed on the SPE.  For all other elements, it is not set.
+//
+
+#define NUM_ROWS      4  // The number of data rows each chare has
+#define NUM_COLS      4  // The number of data columns each chare has
+#define NUM_CHARES    2  // The number of chares (per dimension)
+
+#define MAX_ERROR  0.01f  // The value that all errors have to be below for the program to finish
+
+#define DISPLAY_MATRIX  1
+
+
+#define DATA_BUFFER_ROWS   (NUM_ROWS + 2)
+#define DATA_BUFFER_COLS   (NUM_COLS + 2)
+#define DATA_BUFFER_SIZE   (DATA_BUFFER_ROWS * DATA_BUFFER_COLS)
+
+#define DATA_OFFSET        (DATA_BUFFER_COLS + 1)
+
+#define DATA_NORTH_DATA_OFFSET  (1 + DATA_BUFFER_COLS)
+#define DATA_SOUTH_DATA_OFFSET  (DATA_BUFFER_COLS * (DATA_BUFFER_ROWS - 2) + 1)
+#define DATA_EAST_DATA_OFFSET   (DATA_BUFFER_COLS * 2 - 2)
+#define DATA_WEST_DATA_OFFSET   (DATA_BUFFER_COLS + 1)
+
+#define DATA_NORTH_BUFFER_OFFSET  (1)
+#define DATA_SOUTH_BUFFER_OFFSET  (DATA_BUFFER_COLS * (DATA_BUFFER_ROWS - 1) + 1)
+#define DATA_EAST_BUFFER_OFFSET   (DATA_BUFFER_COLS * 2 - 1)
+#define DATA_WEST_BUFFER_OFFSET   (DATA_BUFFER_COLS)
+
+#define GET_DATA_I(x,y)    ( ((y) * DATA_BUFFER_COLS) + (x) )
+#define GET_DATA_X(i)      ( (i) % DATA_BUFFER_COLS )
+#define GET_DATA_Y(i)      ( (i) / DATA_BUFFER_COLS )
+
+#define GET_CHARE_I(x,y)   ( ((y) * NUM_CHARES) + (x) )
+#define GET_CHARE_X(i)     ( (i) % NUM_CHARES )
+#define GET_CHARE_Y(i)     ( (i) / NUM_CHARES )
+
+#define FUNC_DoCalculation   (1)
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Function Prototypes
+
+extern void funcLookup(int funcIndex, void* readWritePtr, int readWriteLen, void* readOnlyPtr, int readOnlyLen, void* writeOnlyPtr, int writeOnlyPtr);
+extern void doCalculation(volatile float* matrixTmp, volatile float* matrix);
+
+
+#endif //__JACOBI_SHARED_H__
diff --git a/examples/charm++/cell/jacobi/main.C b/examples/charm++/cell/jacobi/main.C
new file mode 100644 (file)
index 0000000..19cb12d
--- /dev/null
@@ -0,0 +1,57 @@
+#include <stdio.h>
+
+#include "main.decl.h"
+#include "main.h"
+#include "jacobi.decl.h"
+#include "jacobi_shared.h"
+
+
+Main::Main(CkArgMsg *msg) {
+
+  // Print some header information for the user
+  CkPrintf(" ----- 2D Jacobi for Cell -----\n");
+
+  // Init the member variables
+  iterationCount = 0;
+
+  // Set the mainProxy readonly
+  mainProxy = thisProxy;
+
+  // Create the Jacobi array
+  jArray = CProxy_Jacobi::ckNew(NUM_CHARES * NUM_CHARES);
+
+  // Register a reduction callback with the array
+  CkCallback *cb = new CkCallback(CkIndex_Main::maxErrorReductionClient(NULL), mainProxy);
+  jArray.ckSetReductionClient(cb);
+
+  // Tell the jArray to start the first iteration
+  iterationCount++;
+  jArray.startIteration();
+  #if DISPLAY_MATRIX != 0
+    CkPrintf("Starting Iteration %d...\n", iterationCount);
+  #endif
+}
+
+void Main::maxErrorReductionClient(CkReductionMsg *msg) {
+
+  float maxError = *((float*)(msg->getData()));
+
+  if (iterationCount == 1 || (iterationCount % 10) == 0)
+    CkPrintf("Iteration %d Finished... maxError = %f...\n", iterationCount, maxError);
+
+  if (maxError <= MAX_ERROR) {
+    CkPrintf("final maxError = %f\n", maxError);
+    CkPrintf("final iterationCount = %d\n", iterationCount);
+    CkExit();
+  } else {
+    iterationCount++;
+    jArray.startIteration();
+    #if DISPLAY_MATRIX != 0
+      CkPrintf("Starting Iteration %d...\n", iterationCount);
+    #endif
+  }
+
+}
+
+
+#include "main.def.h"
diff --git a/examples/charm++/cell/jacobi/main.ci b/examples/charm++/cell/jacobi/main.ci
new file mode 100644 (file)
index 0000000..a891b8b
--- /dev/null
@@ -0,0 +1,12 @@
+mainmodule main {
+
+  readonly CProxy_Main mainProxy;
+
+  extern module jacobi;
+
+  mainchare Main {
+    entry Main(CkArgMsg *msg);
+    entry void maxErrorReductionClient(CkReductionMsg *msg);
+  };
+
+};
diff --git a/examples/charm++/cell/jacobi/main.h b/examples/charm++/cell/jacobi/main.h
new file mode 100644 (file)
index 0000000..86d9723
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef __MAIN_H__
+#define __MAIN_H__
+
+#include "main.decl.h"
+#include "jacobi_shared.h"
+
+
+/* readonly */ CProxy_Main mainProxy;
+
+
+class Main : public CBase_Main {
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  // Member Variables
+  private:
+    CProxy_Jacobi jArray;
+    int iterationCount;
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  // Constructor(s) / Destructor
+  public:
+    Main(CkArgMsg *msg);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  // Member Functions
+  public:
+    void maxErrorReductionClient(CkReductionMsg *msg);
+};
+
+
+#endif //__MAIN_H__