Редактирование: MPI решение волнового уравнения

Перейти к: навигация, поиск

Внимание! Вы не авторизовались на сайте. Ваш IP-адрес будет публично видимым, если вы будете вносить любые правки. Если вы войдёте или создадите учётную запись, правки вместо этого будут связаны с вашим именем пользователя, а также у вас появятся другие преимущества.

Правка может быть отменена. Пожалуйста, просмотрите сравнение версий, чтобы убедиться, что это именно те изменения, которые вас интересуют, и нажмите «Записать страницу», чтобы изменения вступили в силу.
Текущая версия Ваш текст
Строка 3: Строка 3:
 
:<math>T'' = c\ddot{T}</math>
 
:<math>T'' = c\ddot{T}</math>
  
В конце программы добавлено однопроцессорное решение для проверки результатов вычислений.
+
В конце программы добавлено однопроцессорное решение для проверки работы программы.
<div class="mw-collapsible mw-collapsed" style="width:100%" >
 
'''Текст программы на языке C++ (разработчик [[Цветков Денис]]):''' <div class="mw-collapsible-content">
 
 
<syntaxhighlight lang="cpp" line start="1" enclose="div">
 
<syntaxhighlight lang="cpp" line start="1" enclose="div">
 
#include <iostream>
 
#include <iostream>
 
#include <stdio.h>
 
#include <stdio.h>
 
#include <math.h>
 
#include <math.h>
#include <ctime>
 
 
#include "include/mpi.h"
 
#include "include/mpi.h"
 
using namespace std;
 
using namespace std;
Строка 31: Строка 28:
 
     MPI_Get_processor_name(processor_name,&namelen);
 
     MPI_Get_processor_name(processor_name,&namelen);
  
     int N = 20000 + 2;            // количество частиц в цепочке, 2 частицы для г.у.
+
     int N = 100 + 2;            // количество частиц в цепочке, 2 частицы для г.у.
     double t = 200;               // общее время расчета стержня
+
     double t = 30;             // общее время расчета стержня
     double dt = 0.01;             // шаг
+
     double dt = 0.01;           // шаг
 
 
    // Измерение времени работы MPI
 
    double startwtime, endwtime, duration_MPI;
 
    if (myid == 0) {
 
        startwtime = MPI_Wtime();
 
    }
 
  
 
     // здесь задаются начальные условия
 
     // здесь задаются начальные условия
     double U[N], V[N], UU[N], VV[N];       // переменные UU[N], VV[N] для сбора конечных результатов
+
     double U[N], V[N];
 
     for (int i = 1; i < N - 1; i++) {
 
     for (int i = 1; i < N - 1; i++) {
         U[i] = 1;
+
         U[i] = 0;
         if (i < N / 4) V[i] = 0;
+
         if (i < N / 2) V[i] = 0;
 
         else V[i] = 0.01;
 
         else V[i] = 0.01;
 
     }
 
     }
  
     int N_per_proc = ceil((N - 2) / numprocs);            // количество частиц на каждый процессор
+
     int N_per_proc = ceil(N / numprocs);            // количество частиц на каждый процессор
  
     for (double tt = 0; tt < t; tt+= dt) {
+
    // расчет системы
 +
     for (double i = 0; i < t; i+= dt) {
 
         // зеркальные Г.У.
 
         // зеркальные Г.У.
 
         U[0] = U[1];
 
         U[0] = U[1];
Строка 60: Строка 52:
 
         // расчет скоростей для данного шага
 
         // расчет скоростей для данного шага
 
         for (int j = 1 + N_per_proc * myid; j <  1 + N_per_proc * (myid + 1); j++) {
 
         for (int j = 1 + N_per_proc * myid; j <  1 + N_per_proc * (myid + 1); j++) {
             V[j] += (U[j + 1] - 2 * U[j] + U[j - 1]) * dt;
+
             if (j < N - 1) {
 +
                V[j] += (U[j + 1] - 2 * U[j] + U[j - 1]) * dt;
 +
            }
 
         }
 
         }
 +
 +
        MPI_Barrier(MPI_COMM_WORLD);            // здесь происходит синхронизация данных
  
 
         // расчет перемещений для данного шага
 
         // расчет перемещений для данного шага
 
         for (int j = 1 + N_per_proc * myid; j <  1 + N_per_proc * (myid + 1); j++) {
 
         for (int j = 1 + N_per_proc * myid; j <  1 + N_per_proc * (myid + 1); j++) {
             U[j] += V[j] * dt;
+
             if (j < N - 1) {
 +
                U[j] += V[j] * dt;
 +
            }
 
         }
 
         }
  
         if (numprocs > 1)
+
         MPI_Barrier(MPI_COMM_WORLD);           // здесь происходит синхронизация данных
        if (myid == 0) {
 
            MPI_Send(&U[N_per_proc], 1, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD);
 
            MPI_Recv(&U[N_per_proc + 1], 1, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
 
        } else if (myid == (numprocs - 1)) {
 
            MPI_Recv(&U[N_per_proc * (numprocs - 1)], 1, MPI_DOUBLE, numprocs - 2, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
 
            MPI_Send(&U[N_per_proc * (numprocs - 1) + 1], 1, MPI_DOUBLE, numprocs - 2, 0, MPI_COMM_WORLD);
 
        } else {
 
            MPI_Recv(&U[N_per_proc * myid], 1, MPI_DOUBLE, myid - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
 
            MPI_Send(&U[N_per_proc * myid + 1], 1, MPI_DOUBLE, myid - 1, 0, MPI_COMM_WORLD);
 
            MPI_Send(&U[N_per_proc * (myid + 1)], 1, MPI_DOUBLE, myid + 1, 0, MPI_COMM_WORLD);
 
            MPI_Recv(&U[N_per_proc * (myid + 1) + 1], 1, MPI_DOUBLE, myid + 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
 
        }
 
 
}
 
}
 
    MPI_Barrier(MPI_COMM_WORLD);            // здесь происходит синхронизация потоков, чтобы избежать состояния гонки
 
MPI_Gather(&U[1 + N_per_proc * myid], N_per_proc, MPI_DOUBLE, &UU[1], N_per_proc, MPI_DOUBLE, 0, MPI_COMM_WORLD);
 
    MPI_Gather(&V[1 + N_per_proc * myid], N_per_proc, MPI_DOUBLE, &VV[1], N_per_proc, MPI_DOUBLE, 0, MPI_COMM_WORLD);
 
    MPI_Barrier(MPI_COMM_WORLD);            // здесь происходит синхронизация потоков, чтобы избежать состояния гонки
 
 
    // Измерение времени работы MPI
 
    if (myid == 0) {
 
        endwtime = MPI_Wtime();
 
        duration_MPI = (endwtime-startwtime);
 
    }
 
  
 
     // Если это главный процесс, вывод полученного результата
 
     // Если это главный процесс, вывод полученного результата
Строка 98: Строка 73:
  
 
         // здесь происходит расчет системы на одном процессоре, для сравнения результатов
 
         // здесь происходит расчет системы на одном процессоре, для сравнения результатов
        clock_t start;
 
        double duration;
 
        start = clock();
 
 
 
         double U1[N], V1[N];
 
         double U1[N], V1[N];
 
         for (int i = 1; i < N - 1; i++) {
 
         for (int i = 1; i < N - 1; i++) {
             U1[i] = 1;
+
             U1[i] = 0;
             if (i < N / 4) V1[i] = 0;
+
             if (i < N / 2) V1[i] = 0;
 
             else V1[i] = 0.01;
 
             else V1[i] = 0.01;
 
         }
 
         }
Строка 121: Строка 92:
 
         }
 
         }
  
        duration = ( clock() - start ) / (double) CLOCKS_PER_SEC;
+
         // вывод скорости узлов стержня (MPI и однопроцессорное решение)
 
 
         // вывод перемещений (однопроцессорное решение и MPI)
 
 
         for (int i = 1; i < N - 1; i++) {
 
         for (int i = 1; i < N - 1; i++) {
             printf("i = %d, U1 = %f, U = %f\n", i, U1[i], UU[i]);
+
             printf("i = %d, V = %f, V1 = %f\n", i, V[i], V1[i]);
 
         }
 
         }
 
        cout <<"duration: "<< duration <<"  duration_MPI: "<< duration_MPI <<'\n';
 
 
     }
 
     }
  
Строка 136: Строка 103:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
</div>
 
</div>
 
 
Результат работы программы (N = 100, t = 500, dt = 0.01):
 
 
[[Файл: Rez mpi.png]]
 
== Реализация распараллеливания ==
 
Для того, чтобы каждый процесс мог производить вычисления параллельно другим процессам, стержень (состоящий из N частиц) делится на количество процессов. Каждый шаг вычисления процессы обмениваются данными о том, что происходит на границах вычисляемого ими участка стержня с помощью функций отправки (''MPI_Send'') и приема (''MPI_Recv'') данных.
 
 
После окончания вычислений каждый процесс имеет у себя участок памяти с результатами вычислений принадлежащего процессу участка стержня. Для объединения этих данных используется функция ''MPI_Gather'', принцип работы данной функции отображен на рисунке ниже.
 
 
[[Файл: Image003.jpg]]
 
 
После сбора данных многопроцессорного вычисления они выводятся вместе с данными, вычисленными с помощью одного процесса, в сравнительной таблице.
 
 
Чтобы сбор и вывод данных управляющим процессом (''root''-процессом) не начался до того, как остальные процессы завершат свои вычисления, используется функция барьерной синхронизации ''MPI_Barrier'', которая блокирует работу вызвавшего ее процесса до тех пор, пока все другие процессы группы также не вызовут эту функцию. Завершение работы этой функции возможно только всеми процессами одновременно.
 
 
Для правильного расчета требуется, чтобы N - 2 было кратно количеству процессов (например, при 16 процессах можно использовать N = 32000 + 2).
 
 
== Сравнение времени работы программы ==
 
 
Время работы программы:
 
{| border=1 style="text-align:center"
 
!bgcolor=#DDDDDD rowspan="2"|Процессор
 
!bgcolor=#DDDDDD colspan="2"|Время, затраченное на частицу за шаг, нс
 
!bgcolor=#DDDDDD rowspan="2"|~Ускорение, %
 
|-
 
!bgcolor=#DDDDDD|1 процесс
 
!bgcolor=#DDDDDD|MPI
 
|-
 
!bgcolor=#DDDDDD|Intel Core i5-3317U CPU 1.70 GHz, 2 ядра
 
|8.41
 
|4.99
 
!bgcolor=#FFFFBB|69
 
|-
 
!bgcolor=#DDDDDD|Суперкомпьютер, 48 ядер
 
|3.02
 
|0.131
 
!bgcolor=#FFFFBB|2205
 
|-
 
|}
 
 
 
График времени работы программы в зависимости от количества задействованных ядер суперкомпьютера:
 
 
[[Файл: T Nproc.png]]
 
Вам запрещено изменять защиту статьи. Edit Создать редактором

Обратите внимание, что все добавления и изменения текста статьи рассматриваются как выпущенные на условиях лицензии Public Domain (см. Department of Theoretical and Applied Mechanics:Авторские права). Если вы не хотите, чтобы ваши тексты свободно распространялись и редактировались любым желающим, не помещайте их сюда.
Вы также подтверждаете, что являетесь автором вносимых дополнений или скопировали их из источника, допускающего свободное распространение и изменение своего содержимого.
НЕ РАЗМЕЩАЙТЕ БЕЗ РАЗРЕШЕНИЯ МАТЕРИАЛЫ, ОХРАНЯЕМЫЕ АВТОРСКИМ ПРАВОМ!

To protect the wiki against automated edit spam, we kindly ask you to solve the following CAPTCHA:

Отменить | Справка по редактированию  (в новом окне)