Layouts#

Construction and usage of various data layouts.

#include <cstdlib>
#include <iostream>
#include <list>
#include <vector>
#include <numeric>
#include <mpl/mpl.hpp>

template<typename I>
void print_range(const char *const str, I i_1, I i_2) {
  std::cout << str;
  while (i_1 != i_2) {
    std::cout << (*i_1);
    ++i_1;
    std::cout << ((i_1 != i_2) ? ' ' : '\n');
  }
}

int main() {
  const mpl::communicator &comm_world{mpl::environment::comm_world()};
  // run the program with two or more processes
  if (comm_world.size() < 2)
    return EXIT_FAILURE;

  // test layout for a piece of contiguous memory
  if (comm_world.rank() == 0) {
    std::vector<int> v(20);
    std::iota(v.begin(), v.end(), 1);   // fill vector with some data
    mpl::contiguous_layout<int> l(10);  // contiguous_layout with 10 elements
    comm_world.send(v.data(), l, 1);    // send data to rank 1
  }
  if (comm_world.rank() == 1) {
    std::vector<int> v(20, 0);
    mpl::contiguous_layout<int> l(10);  // contiguous_layout with 10 elements
    comm_world.recv(v.data(), l, 0);    // receive data from rank 0
    print_range("v = ", v.begin(), v.end());
  }

  // test layout for a piece of contiguous memory
  // contiguous_layout and vector_layout almost equivalent
  // contiguous_layout has some additional internal bookkeeping
  if (comm_world.rank() == 0) {
    std::vector<int> v(20);
    std::iota(v.begin(), v.end(), 1);   // fill vector with some data
    mpl::contiguous_layout<int> l(10);  // contiguous_layout with 10 elements
    comm_world.send(v.data(), l, 1);    // send data to rank 1
  }
  if (comm_world.rank() == 1) {
    std::vector<int> v(20, 0);
    mpl::vector_layout<int> l(10);    // vector_layout with 10 elements
    comm_world.recv(v.data(), l, 0);  // receive data from rank 0
    print_range("v = ", v.begin(), v.end());
  }

  // test layout for pieces of contiguous memory (equally spaced blocks of constant size)
  // layouts on sending and receiving side may differ but must be compatible
  if (comm_world.rank() == 0) {
    std::vector<int> v(20);
    std::iota(v.begin(), v.end(), 1);      // fill vector with some data
    mpl::contiguous_layout<int> l(3 * 4);  // contiguous_layout with 10 elements
    comm_world.send(v.data(), l, 1);       // send data to rank 1
  }
  if (comm_world.rank() == 1) {
    std::vector<int> v(20, 0);
    mpl::strided_vector_layout<int> l(3,   // number of blocks
                                      4,   // block length
                                      6);  // block spacing
    comm_world.recv(v.data(), l, 0);       // receive data from rank 0
    print_range("v = ", v.begin(), v.end());
  }

  // test layout for a sequence of blocks of memory of varying block length
  // layouts on sending and receiving side may differ but must be compatible
  if (comm_world.rank() == 0) {
    std::vector<int> v(20);
    std::iota(v.begin(), v.end(), 1);          // fill vector with some data
    mpl::contiguous_layout<int> l(3 + 4 + 2);  // contiguous_layout with 9 elements
    comm_world.send(v.data(), l, 1);           // send data to rank 1
  }
  if (comm_world.rank() == 1) {
    std::vector<int> v(20, 0);
    mpl::indexed_layout<int> l({
        {3, 1},  // 1st block of length 3 with displacement 1
        {4, 8},  // 2nd block of length 4 with displacement 8
        {2, 16}  // 3rd block of length 2 with displacement 16
    });
    comm_world.recv(v.data(), l, 0);  // receive data from rank 0
    print_range("v = ", v.begin(), v.end());
  }

  // test layout for a sequence of blocks of memory of constant block length
  // layouts on sending and receiving side may differ but must be compatible
  if (comm_world.rank() == 0) {
    std::vector<int> v(20);
    std::iota(v.begin(), v.end(), 1);      // fill vector with some data
    mpl::contiguous_layout<int> l(3 * 3);  // contiguous_layout with 9 elements
    comm_world.send(v.data(), l, 1);       // send data to rank 1
  }
  if (comm_world.rank() == 1) {
    std::vector<int> v(20, 0);
    mpl::indexed_block_layout<int> l(3,          // block length
                                     {1, 8, 12}  // block displacements
    );
    comm_world.recv(v.data(), l, 0);  // receive data from rank 0
    print_range("v = ", v.begin(), v.end());
  }

  // test layouts of layouts
  // the layouts on sending and receiving side may differ but must be compatible
  if (comm_world.rank() == 0) {
    std::vector<int> v(3 * 3 * 4);
    std::iota(v.begin(), v.end(), 1);          // fill vector with some data
    mpl::contiguous_layout<int> l(3 * 3 * 4);  // contiguous_layout with 36 elements
    comm_world.send(v.data(), l, 1);           // send data to rank 1
  }
  if (comm_world.rank() == 1) {
    std::vector<int> v(15 * 4, 0);
    // layout consists of 3 blocks of length 3 (3 * 3 = 9 ints in total)
    mpl::indexed_block_layout<int> l1(3,          // block length
                                      {1, 8, 12}  // block displacements
    );
    // the layout l1 starts with a hole, by default this hole is ignored when combining
    // several versions of l1, thus we explicitly set its lower bound to 0 and its
    // extent to 15 (= start of last block plus block length)
    l1.resize(0, 15);
    // concatenate 4 indexed layouts, the resulting layout holds 3 * 3 * 4 ints
    mpl::vector_layout<int> l2(4, l1);  // vector layout of l1
    comm_world.recv(v.data(), l2, 0);   // receive data from rank 0
    print_range("v = ", v.begin(), v.end());
  }

  // test layout for a sequence of items
  // layouts on sending and receiving side may differ but must be compatible
  if (comm_world.rank() == 0) {
    std::vector<int> v(20);
    std::iota(v.begin(), v.end(), 1);   // fill vector with some data
    mpl::contiguous_layout<int> l(20);  // contiguous_layout with 9 elements
    comm_world.send(v.data(), l, 1);    // send data to rank 1
  }
  if (comm_world.rank() == 1) {
    std::list<int> v(20, 0);
    mpl::iterator_layout<int> l(v.begin(), v.end());
    comm_world.recv(&(*v.begin()), l, 0);  // receive data from rank 0
    print_range("v = ", v.begin(), v.end());
  }
  // test layout for a sequence of items of different types
  // layouts on sending and receiving side may differ but must be compatible
  if (comm_world.rank() == 0) {
    double y{1};
    std::pair<int, double> pair{2, 3.4};
    const std::vector<double> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    mpl::vector_layout<double> lv(v.size());
    mpl::heterogeneous_layout l(
        y, pair, mpl::make_absolute(v.data(), lv));  // heterogeneous_layout with 3 elements
    comm_world.send(mpl::absolute, l, 1);            // send data to rank 1
  }
  if (comm_world.rank() == 1) {
    double y{0};
    std::pair<int, double> pair;
    std::vector<double> v(10);
    mpl::vector_layout<double> lv(v.size());
    mpl::heterogeneous_layout l(
        y, pair, mpl::make_absolute(v.data(), lv));  // heterogeneous_layout with 3 elements
    comm_world.recv(mpl::absolute, l, 0);            // receive data from rank 0
    std::cout << "y = " << y << "  pair = " << pair.first << ", " << pair.second << "  ";
    print_range("v = ", v.begin(), v.end());
  }
  return EXIT_SUCCESS;
}