Automata: Implementation

Automata are implemented as a mixture of managed and unmanaged C++. The user interface is entirely in managed code, with the calculation engine being a mixture of both.

The abstract class Evolution (Example 11-16) serves as a base class for the two implementations, SequentialEvolution (Example 11-17) and ParallelEvolution (Example 11-18). The actual generational calculations are performed by iterative calls to the Cell class, found in Cell.h and Cell.cpp.

The sequential calculation loop can be found in SequentialEvolution::Step(), shown in Example 11-13.

Example 11-13. Automaton: SequentialEvolution step

// SequentialEvolution::Step() - overrideof step method
void SequentialEvolution::Step()
{
  Cell cell;

  for (int i=0; i<m_size; i++) {
    *(m_dest+i) = cell.CalculateState(
                      m_matrix->data,   // pointer to source data block
                      m_matrix->width,  // logical width of field
                      m_matrix->height, // logical height of field
                      i                 // number of cell position to examine
                      );
  }
}

This loop calls cell.CalculateState() for each cell in the source grid, and puts the resultant state in the same cell location in the destination grid. The corresponding parallel calculation can be found in ParallelEvolution::Step(), as shown in Example 11-14.

Example 11-14. Automaton: ParallelEvolution step

//
// ParallelEvolution::Step() - override of Step method
//
void ParallelEvolution::Step()
{
  size_t begin = 0;        // beginning cell position
  size_t end = m_size-1;   // ending cell position
  size_t grainSize = 4000; // grain (chunk) size for individual tasks

  // set matrix pointers
  tbb_parallel_task::set_values(m_matrix, m_dest);

  // do calculation loop
parallel_for (blocked_range<size_t> (begin, end, grainSize),
              tbb_parallel_task());
}

The routine sets up the parameters of the parallel_for (Chapter 3) by setting begin and end, and specifying the size of a parallel piece of work (grainsize=4000) to be assigned to a thread by the underlying Threading Building Blocks logic. The actual loop code is implementead in the parallel class tbb_parallel_task as an override of the () operator. That operator override code is shown in Example 11-15 as part of the definition of tbb_parallel_task.

Example 11-15. Automaton tbb_parallel_task

//
// class tbb_parallel_task
//
// TBB requires a class for parallel loopimplementations. The actual
// loop "chunks" are performed using the () operator of the class. The
// blocked_range contains the range to calculate. Please see the TBB
// documentation for more information.
//
public class tbb_parallel_task
{
public:
  static void set_values(Matrix* source, char* dest)
  {
    void* x;
    m_source = source;
    m_dest = dest;
    x = m_source;
    x = m_dest;
    return;
  }

  void operator()( const blocked_range<size_t>& r ) const
  {
    int begin = (int)r.begin();// capture lower range number for this chunk
    int end = (int)r.end();    // capture upper range number for this chunk
    Cell cell;

    for (int i=begin; i<=end; i++)
    {
      *(m_dest+i) = cell.CalculateState(
                        m_source->data,  // pointer to source data block
                        m_source->width, // logical width of field
                        m_source->height,// logical height of field
                        i                // number of cell position to examine
                       );
    }
  }

  // constructor
  tbb_parallel_task ( ) {}

private:
  // private data

  static Matrix* m_source;
  static char* m_dest;
};

Threading Building Blocks decides at runtime how many individual tasks to create. It schedules those tasks on threads as it sees fit, passing in the limits of the subloop to perform via the blocked_range parameter (Chapter 3). The more processing cores that are available, the more tasks it will create and the more threads it will utilize. Thus, it scales to fit the local hardware configuration.

Example 11-16. Automaton abstract class evolution

//
// Evolution constructor
//
Evolution::Evolution(
      Matrix *m,         // beginning matrix including initial pattern
      Board^ board,      // the board to update
      HWND messageWindow // window to which WM_DISPLAY_MATRIX message
                         // will be sent
      ) : m_matrix(m), m_board(board), m_hWnd(messageWindow), m_dest(NULL),
          m_size(m_matrix->height * m_matrix->width)
{
  // allocate memory for second matrix data block
  m_dest = new char[m_size];
  m_done = false;
}

//
// Evolution destructor
//
Evolution::~Evolution()
{
  // release allocated memory
  delete m_dest;
}

//
// Evolution::PrepareForCalculation()
// moves the previous destination data to the
// source data block and zeros out destination.
//
void Evolution::PrepareForCalculation()
{
  for (int i=0; i<m_size; i++) {
    *(m_matrix->data+i) = *(m_dest+i);
    *(m_dest+i) = 0;
  }
}

Example 11-17. Automaton SequentialEvolution

//
// SequentialEvolution constructor
//
SequentialEvolution::SequentialEvolution(Matrix *m, Board^ board,
                                         HWND messageWindow)
                     : Evolution(m, board, messageWindow)
{
}

//
// SequentialEvolution::Run - begins looped evolution
//
void SequentialEvolution::Run()
{
  // copy source matrix to destination matrix to set up data for call to
  // PrepareForCalculation().
  for (int i=0; i<m_size; i++) {
    *(m_dest+i) = *(m_matrix->data+i);
  }

  while (!m_done)
  {
    PrepareForCalculation();
    Step();
    m_board->draw();
  }
}

//
// SequentialEvolution::Step() - overrideof step method
//
void SequentialEvolution::Step()
{
  Cell cell;

  for (int i=0; i<m_size; i++) {
    *(m_dest+i) = cell.CalculateState(
                      m_matrix->data,    // pointer to source data block
                      m_matrix->width,  // logical width of field
                      m_matrix->height,  // logical height of field
                      i          // number of cell position to examine
                      );
  }
}

Example 11-18. Automaton ParallelEvolution

//
// ParallelEvolution constructor
//
ParallelEvolution::ParallelEvolution(Matrix *m, Board^ board,
                                     HWND messageWindow)
                   : Evolution(m, board, messageWindow)
{
  // instantiate a task_scheduler_init object and save a pointer to it
  m_pInit = NULL;
}

//
// ParallelEvolution destructor
//
ParallelEvolution::~ParallelEvolution()
{
  // delete task_scheduler_init object
  if (m_pInit != NULL)
    delete m_pInit;
}

//
// ParallelEvolution::Run - begins looped evolution
//
void ParallelEvolution::Run()
{
  // start task scheduler as necessary
  if (m_pInit == NULL)
    m_pInit = new task_scheduler_init();

  // copy source matrix to destination matrix to set up data for call to
  // PrepareForCalculation().
  for (int i=0; i<m_size; i++)
  {
    *(m_dest+i) = *(m_matrix->data+i);
  }

  while (!m_done)
  {
    PrepareForCalculation();
    Step();
    m_board->draw();
  }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.152.162