Advertisement
filashkov

Untitled

Dec 19th, 2021
854
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 14.15 KB | None | 0 0
  1. /* Include benchmark-specific header. */
  2. #include "heat-3d.h"
  3. #include <unistd.h>
  4. #include <mpi.h>
  5.  
  6. double bench_t_start, bench_t_end;
  7.  
  8. static
  9. double rtclock()
  10. {
  11.     struct timeval Tp;
  12.     int stat;
  13.     stat = gettimeofday(&Tp, NULL);
  14.     if (stat != 0) {
  15.         printf("Error return from gettimeofday: %d", stat);
  16.     }
  17.     return (Tp.tv_sec + Tp.tv_usec * 1.0e-6);
  18. }
  19.  
  20. void
  21. bench_timer_start()
  22. {
  23.     bench_t_start = rtclock();
  24. }
  25.  
  26. void
  27. bench_timer_stop()
  28. {
  29.     bench_t_end = rtclock();
  30. }
  31.  
  32. void
  33. old_bench_timer_print()
  34. {
  35.     printf("Time in seconds = %0.6lf\n", bench_t_end - bench_t_start);
  36. }
  37.  
  38. void
  39. bench_timer_print()
  40. {
  41.     printf("%0.6lf\n", bench_t_end - bench_t_start);
  42. }
  43.  
  44. static
  45. void init_subarray(int n, int n1, int n2, int n3,
  46.         double A[n1][n2][n3], double B[n1][n2][n3],
  47.         int i_offset, int j_offset, int k_offset,
  48.         int i_stop, int j_stop, int k_stop)
  49. {
  50.     i_offset = (i_offset < 0) ? 0 : i_offset;
  51.     j_offset = (j_offset < 0) ? 0 : j_offset;
  52.     k_offset = (k_offset < 0) ? 0 : k_offset;
  53.  
  54.     for (int i = i_offset; i < i_stop; i++) {
  55.         for (int j = j_offset; j < j_stop; j++) {
  56.             for (int k = k_offset; k < k_stop; k++) {
  57.                 A[i][j][k] = B[i][j][k] = (double)(i + j + (n - k)) * 10 / (n);
  58.             }
  59.         }
  60.     }
  61. }
  62.  
  63. static
  64. void print_array(int n,
  65.         double A[n][n][n])
  66. {
  67.     fprintf(stderr, "==BEGIN DUMP_ARRAYS==\n");
  68.     fprintf(stderr, "begin dump: %s", "A");
  69.     for (int i = 0; i < n; i++) {
  70.         for (int j = 0; j < n; j++) {
  71.             for (int k = 0; k < n; k++) {
  72.                 if ((i * n * n + j * n + k) % 20 == 0) {
  73.                     fprintf(stderr, "\n");
  74.                 }
  75.                 fprintf(stderr, "%0.2lf ", A[i][j][k]);
  76.             }
  77.         }
  78.     }
  79.     fprintf(stderr, "\nend   dump: %s\n", "A");
  80.     fprintf(stderr, "==END   DUMP_ARRAYS==\n");
  81. }
  82.  
  83. static
  84. void kernel_heat_3d(int tsteps,
  85.         int n,
  86.         double A[n][n][n],
  87.         double B[n][n][n])
  88. {
  89.     for (int t = 1; t <= TSTEPS; t++) {
  90.         for (int i = 1; i < n - 1; i++) {
  91.             for (int j = 1; j < n - 1; j++) {
  92.                 for (int k = 1; k < n - 1; k++) {
  93.                     B[i][j][k] = 0.125 * (A[i + 1][j][k] - 2.0 * A[i][j][k] + A[i - 1][j][k])
  94.                             + 0.125 * (A[i][j + 1][k] - 2.0 * A[i][j][k] + A[i][j - 1][k])
  95.                             + 0.125 * (A[i][j][k + 1] - 2.0 * A[i][j][k] + A[i][j][k - 1])
  96.                             + A[i][j][k];
  97.                 }
  98.             }
  99.         }
  100.         for (int i = 1; i < n - 1; i++) {
  101.             for (int j = 1; j < n - 1; j++) {
  102.                 for (int k = 1; k < n - 1; k++) {
  103.                     A[i][j][k] = 0.125 * (B[i + 1][j][k] - 2.0 * B[i][j][k] + B[i - 1][j][k])
  104.                             + 0.125 * (B[i][j + 1][k] - 2.0 * B[i][j][k] + B[i][j - 1][k])
  105.                             + 0.125 * (B[i][j][k + 1] - 2.0 * B[i][j][k] + B[i][j][k - 1])
  106.                             + B[i][j][k];
  107.                 }
  108.             }
  109.         }
  110.     }
  111. }
  112.  
  113.  
  114. void
  115. test(int data_size, int* argc_address, char*** argv_address)
  116. {
  117.     int n = data_size;
  118.     int tsteps = TSTEPS;
  119.  
  120.     MPI_Init(argc_address, argv_address);
  121.  
  122.     const int ndims = 3;
  123.     int rank, procs;
  124.     MPI_Comm cart_comm;
  125.     int reorder;
  126.     int coord[ndims], id;
  127.     int right, down, left, up, next, previous;
  128.  
  129.     MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  130.     MPI_Comm_size(MPI_COMM_WORLD, &procs);
  131.  
  132.     /*
  133.     Мы хотим разделить наш 3-х мерный тензор на кубики, которые мы будем считать
  134.     на каждом из процессоров. Сколько будем кубиков (в зависимости от числа
  135.     процессов) по каждой из размерностей можно посчитать с помошью MPI_Dims_create,
  136.     хотя вручную тоже можно было это написать.
  137.     */
  138.  
  139.     int dim[/*ndims*/] = { 0, 0, 0 };
  140.     MPI_Dims_create(procs, ndims, dim);
  141.  
  142.     /*
  143.     Периодичность по какой-то из размерностей означает, что следующим после
  144.     последнего будет первый элемент. У нас ни по одной размерностей периодичностей
  145.     нет.
  146.     */
  147.    
  148.     int periodical[/*ndims*/] = { 0, 0, 0 };
  149.     reorder = 0;
  150.  
  151.     MPI_Cart_create(MPI_COMM_WORLD, ndims, dim, periodical, reorder, &cart_comm);
  152.     MPI_Cart_coords(cart_comm, rank, ndims, coord);
  153.  
  154.     // Удобный доступ к рангу соседей по каждому из направлений:
  155.     MPI_Cart_shift(cart_comm, 0, 1, &up, &down);
  156.     MPI_Cart_shift(cart_comm, 1, 1, &left, &right);
  157.     MPI_Cart_shift(cart_comm, 2, 1, &previous, &next);
  158.  
  159.     // + 2 нужно для теневых граней
  160.     int n1 = n / dim[0] + 2, n2 = n / dim[1] + 2, n3 = n / dim[2] + 2;
  161.    
  162.     // Не всегда n % dim[i] == 0 (i = 0, 1, 2)
  163.     if (down == -1) {
  164.         n1 += n % dim[0];
  165.     }
  166.     if (right == -1) {
  167.         n2 += n % dim[1];
  168.     }
  169.     if (next == -1) {
  170.         n3 += n % dim[2];
  171.     }
  172.  
  173.     /*
  174.     Теперь надо создать на каждом из MPI-процессов A и B и их проинициализировать
  175.     */
  176.     double (*A)[n1][n2][n3]; A = (double(*)[n1][n2][n3])malloc((n1) * (n2) * (n3) * sizeof(double));
  177.     double (*B)[n1][n2][n3]; B = (double(*)[n1][n2][n3])malloc((n1) * (n2) * (n3) * sizeof(double));
  178.  
  179.     // Вычитаем -1 для теневых граней
  180.     int i_offset = n / dim[0] * coord[0] - 1;
  181.     int j_offset = n / dim[1] * coord[1] - 1;
  182.     int k_offset = n / dim[2] * coord[2] - 1;
  183.  
  184.     int i_bound = i_offset + n1;
  185.     int j_bound = j_offset + n2;
  186.     int k_bound = k_offset + n3;
  187.  
  188.     for (int i = i_offset; i < i_bound; i++) {
  189.         for (int j = j_offset; j < j_bound; j++) {
  190.             for (int k = k_offset; k < k_bound; k++) {
  191.                 (*A)[i - i_offset][j - j_offset][k - k_offset]
  192.                         = (*B)[i - i_offset][j - j_offset][k - k_offset]
  193.                         = (double)(i + j + (n - k)) * 10 / (n);
  194.             }
  195.         }
  196.     }
  197.  
  198.     //bench_timer_start();
  199.  
  200.     MPI_Request requests[12]; // У нас 6 shadow edges, один request на send, другой на recieve
  201.     MPI_Status statuses[12];
  202.  
  203.     /*
  204.     Нам придётся передавать грани, то есть 2D подмассивы. Для этого нам понадобится сразу 3 типа.
  205.     Так как элементы разных теневых граней, перпендикулярных разным осям по-разному лежат в памяти.
  206.     */
  207.  
  208.     MPI_Datatype matrix_oxy, matrix_oxz, matrix_oyz;
  209.  
  210.     int sizes[/*ndims*/] = { n1, n2, n3 };
  211.     int subsizes_oxy[/*ndims*/] = { n1, n2, 1 };
  212.     int subsizes_oxz[/*ndims*/] = { n1, 1, n3 };
  213.     int subsizes_oyz[/*ndims*/] = { 1, n2, n3 };
  214.     int starts[/*ndims*/] = { 0, 0, 0 };
  215.  
  216.     MPI_Type_create_subarray(ndims, sizes, subsizes_oxy, starts, MPI_ORDER_C, MPI_DOUBLE, &matrix_oxy);
  217.     MPI_Type_commit(&matrix_oxy);
  218.  
  219.     MPI_Type_create_subarray(ndims, sizes, subsizes_oxz, starts, MPI_ORDER_C, MPI_DOUBLE, &matrix_oxz);
  220.     MPI_Type_commit(&matrix_oxz);
  221.  
  222.     MPI_Type_create_subarray(ndims, sizes, subsizes_oyz, starts, MPI_ORDER_C, MPI_DOUBLE, &matrix_oyz);
  223.     MPI_Type_commit(&matrix_oyz);
  224.  
  225.     double time_start = MPI_Wtime();
  226.  
  227.     for (int t = 1; t <= TSTEPS; t++) {
  228.         for (int i = 1 + (up == -1); i < n1 - 1 - (down == -1); i++) {
  229.             for (int j = 1 + (left == -1); j < n2 - 1 - (right == -1); j++) {
  230.                 for (int k = 1 + (previous == -1); k < n3 - 1 - (next == -1); k++) {
  231.                     (*B)[i][j][k] = 0.125 * ((*A)[i + 1][j][k] - 2.0 * (*A)[i][j][k] + (*A)[i - 1][j][k])
  232.                             + 0.125 * ((*A)[i][j + 1][k] - 2.0 * (*A)[i][j][k] + (*A)[i][j - 1][k])
  233.                             + 0.125 * ((*A)[i][j][k + 1] - 2.0 * (*A)[i][j][k] + (*A)[i][j][k - 1])
  234.                             + (*A)[i][j][k];
  235.                 }
  236.             }
  237.         }
  238.         /*
  239.         У нас поменялся массив B. Соседи должны обновить значения теневых граней.
  240.         Также нужно подождать, пока обмен с соседями не завершится. Ведь для дальнейших вычислений
  241.         нам понадабятся данные, полученные с других процессов
  242.         */
  243.  
  244.         // Получаем данные от верхнего соседа и передаём нижнему (по 1-му измерению)
  245.         MPI_Irecv(&(*B)[0][0][0], 1, matrix_oyz, up, 1215, MPI_COMM_WORLD, &requests[0]);
  246.         MPI_Isend(&(*B)[n1 - 2][0][0], 1, matrix_oyz, down, 1215, MPI_COMM_WORLD, &requests[1]);
  247.  
  248.         // Получаем данные от нижнего соседа и передаём верхнему (по 1-му измерению)
  249.         MPI_Irecv(&(*B)[n1 - 1][0][0], 1, matrix_oyz, down, 1216, MPI_COMM_WORLD, &requests[2]);
  250.         MPI_Isend(&(*B)[1][0][0], 1, matrix_oyz, up, 1216, MPI_COMM_WORLD, &requests[3]);
  251.  
  252.         // Получаем данные от правого соседа и передаём левому (по 2-му измерению)
  253.         MPI_Irecv(&(*B)[0][0][0], 1, matrix_oxz, right, 1217, MPI_COMM_WORLD, &requests[4]);
  254.         MPI_Isend(&(*B)[0][n2 - 2][0], 1, matrix_oxz, left, 1217, MPI_COMM_WORLD, &requests[5]);
  255.  
  256.         // Получаем данные от левого соседа и передаём правому (по 2-му измерению)
  257.         MPI_Irecv(&(*B)[0][n2 - 1][0], 1, matrix_oxz, left, 1218, MPI_COMM_WORLD, &requests[6]);
  258.         MPI_Isend(&(*B)[0][1][0], 1, matrix_oxz, right, 1218, MPI_COMM_WORLD, &requests[7]);  
  259.  
  260.         // Получаем данные от следующего соседа и передаём предыдущему (по 3-му измерению)
  261.         MPI_Irecv(&(*B)[0][0][0], 1, matrix_oxy, next, 1219, MPI_COMM_WORLD, &requests[8]);
  262.         MPI_Isend(&(*B)[0][0][n3 - 2], 1, matrix_oxy, previous, 1219, MPI_COMM_WORLD, &requests[9]);
  263.  
  264.         // Получаем данные от предыдущего соседа и передаём следующему (по 3-му измерению)
  265.         MPI_Irecv(&(*B)[0][0][n3 - 1], 1, matrix_oxy, previous, 1220, MPI_COMM_WORLD, &requests[10]);
  266.         MPI_Isend(&(*B)[0][0][1], 1, matrix_oxy, next, 1220, MPI_COMM_WORLD, &requests[11]);  
  267.  
  268.         // Дожидаемся получения всех теневых граней
  269.         MPI_Waitall(12, requests, statuses);
  270.  
  271.         for (int i = 1 + (up == -1); i < n1 - 1 - (down == -1); i++) {
  272.             for (int j = 1 + (left == -1); j < n2 - 1 - (right == -1); j++) {
  273.                 for (int k = 1 + (previous == -1); k < n3 - 1 - (next == -1); k++) {
  274.                     (*A)[i][j][k] = 0.125 * ((*B)[i + 1][j][k] - 2.0 * (*B)[i][j][k] + (*B)[i - 1][j][k])
  275.                             + 0.125 * ((*B)[i][j + 1][k] - 2.0 * (*B)[i][j][k] + (*B)[i][j - 1][k])
  276.                             + 0.125 * ((*B)[i][j][k + 1] - 2.0 * (*B)[i][j][k] + (*B)[i][j][k - 1])
  277.                             + (*B)[i][j][k];
  278.                 }
  279.             }
  280.         }
  281.  
  282.         /*
  283.         Теперь поменялся массив A. Нужно обновить значение теневых граней.
  284.         */
  285.  
  286.         MPI_Irecv(&(*A)[0][0][0], 1, matrix_oyz, up, 1221, MPI_COMM_WORLD, &requests[0]);
  287.         MPI_Isend(&(*A)[n1 - 2][0][0], 1, matrix_oyz, down, 1221, MPI_COMM_WORLD, &requests[1]);
  288.  
  289.         // Получаем данные от нижнего соседа и передаём верхнему (по 1-му измерению)
  290.         MPI_Irecv(&(*A)[n1 - 1][0][0], 1, matrix_oyz, down, 1222, MPI_COMM_WORLD, &requests[2]);
  291.         MPI_Isend(&(*A)[1][0][0], 1, matrix_oyz, up, 1222, MPI_COMM_WORLD, &requests[3]);
  292.  
  293.         // Получаем данные от правого соседа и передаём левому (по 2-му измерению)
  294.         MPI_Irecv(&(*A)[0][0][0], 1, matrix_oxz, right, 1223, MPI_COMM_WORLD, &requests[4]);
  295.         MPI_Isend(&(*A)[0][n2 - 2][0], 1, matrix_oxz, left, 1223, MPI_COMM_WORLD, &requests[5]);
  296.  
  297.         // Получаем данные от левого соседа и передаём правому (по 2-му измерению)
  298.         MPI_Irecv(&(*A)[0][n2 - 1][0], 1, matrix_oxz, left, 1224, MPI_COMM_WORLD, &requests[6]);
  299.         MPI_Isend(&(*A)[0][1][0], 1, matrix_oxz, right, 1224, MPI_COMM_WORLD, &requests[7]);  
  300.  
  301.         // Получаем данные от следующего соседа и передаём предыдущему (по 3-му измерению)
  302.         MPI_Irecv(&(*A)[0][0][0], 1, matrix_oxy, next, 1225, MPI_COMM_WORLD, &requests[8]);
  303.         MPI_Isend(&(*A)[0][0][n3 - 2], 1, matrix_oxy, previous, 1225, MPI_COMM_WORLD, &requests[9]);
  304.  
  305.         // Получаем данные от предыдущего соседа и передаём следующему (по 3-му измерению)
  306.         MPI_Irecv(&(*A)[0][0][n3 - 1], 1, matrix_oxy, previous, 1226, MPI_COMM_WORLD, &requests[10]);
  307.         MPI_Isend(&(*A)[0][0][1], 1, matrix_oxy, next, 1226, MPI_COMM_WORLD, &requests[11]);  
  308.  
  309.         // Дожидаемся получения всех теневых граней
  310.         MPI_Waitall(12, requests, statuses);
  311.     }
  312.  
  313.     double time_end = MPI_Wtime();
  314.     if (rank == 0) {
  315.         printf("n, procs, time = %d, %d, %lf\n", n, procs, time_end - time_start);
  316.     }
  317.  
  318.     /*
  319.     if (argc > 42 && !strcmp(argv[0], "")) {
  320.         print_array(n, *A);
  321.     }
  322.     */
  323.  
  324.     free((void*)A);
  325.     free((void*)B);
  326.  
  327.     MPI_Finalize();
  328. }
  329.  
  330. int
  331. main(int argc, char** argv)
  332. {
  333.     int data_size[] = { 100 };
  334.     int data_sizes_length = sizeof(data_size) / sizeof(*data_size);
  335.  
  336.     for (int j = 0; j < data_sizes_length; j++) {
  337.         printf("%d\n", data_size[j]);
  338.         test(data_size[j], &argc, &argv);
  339.     }
  340.     return 0;
  341. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement