- •Оглавление
- •Поиск простых чисел
- •Описание работы
- •Параллельный алгоритм # 4: последовательный перебор простых чисел
- •Синхронизация доступа к одноэлементному буферу Описание работы
- •Синхронизация приоритетного доступа к многоэлементному буферу Описание работы
- •Методические указания
- •Клеточная модель "игра жизнь" дж. Конвея Описание работы
- •Трансляция ОрепМр-программ
- •Метод трапеций
- •Метод Симпсона
Клеточная модель "игра жизнь" дж. Конвея Описание работы
Клеточная модель представляет собой множество клеток (ячеек таблицы), принимающих одно из нескольких состояний. Состояние каждой клетки определяется состоянием её ближайших соседей. Одной из известных моделей является "Игра Жизнь" математика Дж. Конвея.
В модели Конвея каждая клетка может находиться в двух состояниях: живая или неживая. Состояние клетки на следующем шаге определяется потенциалом клетки (числом живых соседних клеток):
если потенциал клетки равен двум, то клетка сохраняет свое состояние;
если потенциал равен трем, то клетка оживает;
если потенциал меньше двух или больше трех, то клетка погибает.
Правила изменения состояния клетки можно описать следующим лямбда-выражением:
varlifeRules = new Func<int, bool, bool>((p, state) =>
{
if(p ==3) return true; else if (p ==2) return state; else
return false;
});
Последовательный алгоритм расчета представляет собой расчет состояния каждой клетки:
LifeTabletableNew = new LifeTable; for(inti=0; i< Height; i++) for(intj=0; j < Width; j++)
{
p = CalcPotential(table[i,j]);
tableNew[i, j] = lifeRules(p, table[i,j]);
}
table = tableNew;
Потенциал клетки вычисляется по восьми ближайшим соседям клетки.
intCalcPotential(inti, int j)
{
int p=0;
for(intx = i-1; x <= i + 1; x++) for (int у =j-l; у <=j + l;y++)
{
if(x <0||j<0||x> = Height \ у > = Width | (x == i&&у ==j)) continue;
if(table[x,y]) p++;
}
returnp;
}
Вычисления новых состояний для каждой клетки являются независимыми и могут выполняться параллельно.
Parallel.For(0, Height, (i) => { for(intj=0; j < Width; j++)
{
p = CalcPotential(table[i,j]); tableNew[i, j] = lifeRules(p, table[i,j]);
}
});
Также можно изменить порядок расчета и распараллелить внешний цикл по столбцам:
Parallel. For(0, Width, (j) => { for(inti=0; i< Height; i++)
{
р = CalcPotential(table [i,j]); tctbleNew[i, j] = lifeRules(p, table[i,j]);
}}
Вывод таблицы состояний клеточной модели может быть следующим:
Рисунок
5.1.

ЗАДАНИЯ К ЛАБОРАТОРНОЙ РАБОТЕ № 5
Реализуйте Windows-приложение, которое последовательно отображает состояния клеточной модели "Игра жизнь".
Реализуйте последовательный алгоритм расчета состояний модели.
Реализуйте параллельные алгоритмы расчета состояний модели. Для распараллеливания используйте задачи (tasks) или метод Parallel.For.
Реализуйте возможность отмены расчета с помощью объекта CancellationToken.
Выполните анализ эффективности разработанных алгоритмов.
КОНТРОЛЬНЫЕ ВОПРОСЫ
Имеет ли смысл распараллеливание внутреннего цикла расчета? Почему?
for(inti=0; i< Height; i++)
{
Parallel.For(0, Width, j=>
{
p = CalcPotential(table[i,j]); tableNew[i, j] = lifeRules(p, table[i,j]);
});
}
Как вариант расчета - по строкам или по столбцам - более эффективен и с чем это связано?
Продумайте вариант блочной декомпозиции, где блок выступает матрицей размера К х К. В чем достоинства и недостатки блочной декомпозиции для этой задачи? Какое значение параметра К следует выбирать?
ЛАБОРАТОРНАЯ РАБОТА № 6
РАСПАРАЛЛЕЛИВАНИЕ ПРОГРАММЫ ЫЧИСЛЕНИЯ ОПРЕДЕЛЕННОГО ИНТЕГРАЛА С ПОМОЩЬЮ ОрепМР
Цель работы - распараллеливание программ с помощью ОрепМР.
Параллельная ОрепМР-программа состоит из последовательных и параллельных секций. Границы параллельных секций обозначаются директивами ОрепМР. Процесс разработки ОрепМР-программы включает следующие этапы:
Разработка последовательной программы;
Выявление участков потенциального параллелизма. Чаще всего это циклы;
Анализ трудоемкости параллельных секций (профилирование программы). Наибольший выигрыш в производительности дает распараллеливание секций, на которые приходятся наибольшие затраты процессорного времени;
Пошаговое распараллеливание программы, начиная с наиболее трудоемких секций.
Профилирование может производиться как с помощью специальных программных инструментов, так и простыми средствами, например, с помощью вызова специальных подпрограмм-таймеров, размещенных в различных местах программы.
Цикл эффективно распараллеливается, если отсутствуют перекрестные зависимости между его итерациями. Избавиться от таких зависимостей иногда можно, выполнив преобразование цикла.
Необходимо правильно определить область видимости переменных в параллельных секциях программы. Параметр цикла, например, должен быть объявлен локальной переменной. Инвариант цикла (величина, не изменяющаяся при выполнении итераций цикла) должен быть глобальным.
При вычислении суммы, например, к переменной, которая используется для "накопления" суммы, должна быть применена операция приведения (редукции).
Следует обратить внимание на синхронизацию вычислений. По умолчанию в циклах используется барьерная синхронизация. Наличие синхронизаций увеличивает предсказуемость поведения программы, но замедляет ее работу.
Дополнительный выигрыш в производительности дает объединение нескольких параллельных секций в одну. В этом случае уменьшаются накладные расходы на запуск нитей и их завершение.
