В данном разделе будет рассмотрены несколько способов адресации массивов и описаны необходимые изменения в программе, чтобы привести ее к виду, наиболее подходящему для распараллеливания.
Рассмотрим классическую двумерную задачу теплопроводности, решаемую методом Якоби.
Явный обход двумерной областиНаиболее простой способ реализовать задачу задачу заключается в явном обходе двумерного массива размера I×J:
for (i = 1; i < I - 1; i++) { for (j = 1; j < J - 1; j++) { A_new[i][j] = ( A_old[i+1][j] + A_old[i][j+1] + A_old[i-1][j] + A_old[i][j-1] ) / 4; } }
Этот способ прост, но если нужно обсчитать не прямоугольную область, а область сложной формы, то, во-первых, придется завести массивы на весь объемлющий прямоугольник (лишние данные), и, во-вторых, будет сложно написать правильные циклы.
Этот способ также неудобен для распараллеливания, поэтому необходимо завести структуры, описывающие область.
Описание областиМассивы A_old и A_new теперь одномерные, содержащие все ячейки области. Массив Grid (сетка) хранит информацию о каждой ячейке сетки. А именно, каждый элемент массива — это структура, содержащая:
- логические координаты ячейки: поля i и j;
- является ли ячейка внутренней: поле is_inner.
Подчеркнем, что все массивы A_old, A_new и Grid одинаковой длины. И информация про одну ячейку хранится в элементах с одинаковым индексом.
Так же потребуется функция GetCell, которая по двум координатам i и j вернет номер ячейки в массивах.
Тогда основной цикл запишется следующим образом:
for (n = 0; n < N; n++) { if (Grid[n].is_inner) { // если внутрення ячейка int i = Grid[n].i; // i-ая координата int j = Grid[n].j; // j-ая координата A_new[n] = ( A_old[GetCell(i+1,j)] + A_old[GetCell(i,j+1)] + A_old[GetCell(i-1,j)] + A_old[GetCell(i,j-1)] ) / 4; } }
Данный способ уже лучше применим для распаралленивания. Но еще мешает функция GetCell, от которой нужно избавиться, записав номера соседей в массив Grid.
Хранение соседейДанный способ очень похож на предыдущий. В нем в каждом элементе массива Grid хранятся:- логические координаты ячейки: поля i и j;
- является ли ячейка внутренней: поле is_inner;
- номера (в массивах) соседних элементов: поля north, east, south, west.
Тогда цикл запишется следующим образом:
for (n = 0; n < N; n++) { if (Grid[n].is_inner) { // если внутрення ячейка A_new[n] = ( A_old[Grid[n].north] + A_old[Grid[n].east] + A_old[Grid[n].south] + A_old[Grid[n].west] ) / 4; } }
Данный способ подходит для применения распараллеливания методом преобразования массивов, описанным выше. В нем массивы A_old, A_new, Grid вместо всех ячеек (в последовательном варианте) будут хранить свои ячейки для данного процесса.
Если предполагается использовать неструктурные сетки, то можно сделать еще одно обобщение.
Неструктурные сеткиВ случае неструктурных сеток количество соседей может быть разное у разных ячеек. Поэтому вместо жесткого списка соседей в элементах массива Grid необходимо хранить список соседей. А так же для расчета потребуются физические координаты ячеек x и y. В итоге, в элементе массива Grid хранятся:- логические координаты ячейки: поля i и j;
- физические координаты ячейки: поля x и y;
- является ли ячейка внутренней: поле is_inner;
- количество соседей: поле neighbours_num;
- номера соседей: поле neighbours.
В результате цикл запишется следующим образом:
for (n = 0; n < N; n++) { if (Grid[n].is_inner) { // если внутрення ячейка A_new[n] = 0; for (m = 0; m < Grid[n].neighbours_num; m++) { // обход всех соседей A_new[n] += A_old[Grid[n].neighbours[m]]; } A_new[n] /= Grid[n].neighbours_num; // вычисление среднего арифметического значения всех соседей } }
Отметим, что данный пример программы не совсем корректен с физической точки зрения, т.к. не учитывает физические координаты ячеек.
ЗаключениеПосле проведенных модификаций получаем программу, в которой в отдельном массиве Grid хранятся данные о сетке (если сетка не адаптивная, то эти данные не изменяются), а в других массивах (таких как A_new и A_old) хранятся значения физических переменных в ячейках массива.
Когда данные представлены в таком виде, разделение их между параллельно работающими процессами становится обозримой и автоматизируемой задачей. |
|