Seismic

Here is a simple seismic wave simulation (wave propagation) based on parallel_for and blocked_range. The key parts of this example are shown here; the entire code is available for download.

The main program steps through the simulation of a seismic wave in a core loop that sets the impulse from the source of the disturbance, does the two tough computations of stress updates and velocities, and finally cleans up the edges of the simulation.

First we’ll look at the stress algorithm, in Example 11-2 and Example 11-3. The first shows the serial version of the algorithm, and the second shows an equivalent parallel version.

Example 11-2. Seismic serial: Update stress

static void SerialUpdateStress() {
  drawing_area drawing(0, 0, UniverseWidth, UniverseHeight);
  for( int i=1; i<UniverseHeight-1; ++i ) {
    color_t* c = ColorMap[Material[i]];
    drawing.set_pos(1, i);
#pragma ivdep
    for( int j=1; j<UniverseWidth-1; ++j ) {
      S[i][j] += (V[i][j+1]-V[i][j]);
      T[i][j] += (V[i+1][j]-V[i][j]);
      int index = (int)(V[i][j]*(ColorMapSize/2)) + ColorMapSize/2;
      if( index<0 ) index = 0;
      if( index>=ColorMapSize ) index = ColorMapSize-1;
      drawing.put_pixel(c[index]);
    }
  }
}

Example 11-3. Seismic parallel: Update stress

struct UpdateStressBody {
  void operator()( const tbb::blocked_range<int>& range ) const {
    drawing_area drawing(0,
                         range.begin(),
                         UniverseWidth,
                         range.end()-range.begin());
    int i_end = range.end();
    for( int y = 0, i=range.begin(); i!=i_end; ++i,y++ ) {
      color_t* c = ColorMap[Material[i]];
      drawing.set_pos(1, y);
#pragma ivdep
      for( int j=1; j<UniverseWidth-1; ++j ) {
        S[i][j] += (V[i][j+1]-V[i][j]);
        T[i][j] += (V[i+1][j]-V[i][j]);
        int index = (int)(V[i][j]*(ColorMapSize/2)) + ColorMapSize/2;
        if( index<0 ) index = 0;
        if( index>=ColorMapSize ) index = ColorMapSize-1;
        drawing.put_pixel(c[index]);
      }
    }
  }
};

static void ParallelUpdateStress() {
  tbb::parallel_for(
    tbb::blocked_range<int>( 1, UniverseHeight-1, GrainSize ),
                             UpdateStressBody() );
}

The parallel version, shown in Example 11-3, requires very little work to create from the serial version. The serial routine needs to be put into a function in a class. Note that structUpdateStressBody { is equivalent to the class/public we use elsewhere in the book:

	class UpdateStressBody {
	public:

It seems worth letting you see it both ways. The function ParallelUpdateStress simply uses the parallel_for algorithm (Chapter 3), and specifies the range, the struct/class UpdateStressBody we created, and the grain size. The difference between the two versions of the code is minimal. If C++ were to support lambda functions (Chapter 12), the code would look almost identical. Similarly, compare the easy conversion of the serial version in Example 11-4 with the parallel version in Example 11-5.

Example 11-4. Seismic serial: Update velocity

static void SerialUpdateVelocity() {
  for( int i=1; i<UniverseHeight-1; ++i )
#pragma ivdep
    for( int j=1; j<UniverseWidth-1; ++j ) {
      V[i][j] += (S[i][j] - S[i][j-1] + T[i][j] - T[i-1][j])*M[i];
    }
}

Example 11-5. Seismic parallel: Update velocity

struct UpdateVelocityBody {
  void operator()( const tbb::blocked_range<int>& range ) const {
    int i_end = range.end();
    for( int i=range.begin(); i!=i_end; ++i ) {
#pragma ivdep
      for( int j=1; j<UniverseWidth-1; ++j ) {
        V[i][j] += (S[i][j] - S[i][j-1] + T[i][j] - T[i-1][j])*M[i];
      }
    }
  }
};

static void ParallelUpdateVelocity() {
  tbb::parallel_for(
    tbb::blocked_range<int>( 1, UniverseHeight-1, GrainSize ),
                             UpdateVelocityBody() );
}
..................Content has been hidden....................

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