
лаб5 / lab5
.docxГУАП
КАФЕДРА № 41
ОТЧЕТ ЗАЩИЩЕН С ОЦЕНКОЙ
ПРЕПОДАВАТЕЛЬ
Старший преподаватель |
|
|
|
Д.В. Куртяник |
должность, уч. степень, звание |
|
подпись, дата |
|
инициалы, фамилия |
ОТЧЕТ О ЛАБОРАТОРНОЙ РАБОТЕ 5 Параллелизм |
по курсу: ОСНОВЫ ПРОГРАММИРОВАНИЯ |
РАБОТУ ВЫПОЛНИЛ
СТУДЕНТ ГР. № |
4016 |
|
|
|
М.О.Жовтяк |
|
|
|
подпись, дата |
|
инициалы, фамилия |
Санкт-Петербург 2021
Постановка задачи
Цель лабораторной работы: изучение автоматического распараллеливания кода, использующего стандартные алгоритмы библиотеки STL, способов применения и особенностей работы; получение навыков программирования на языке C++.
Задание на программирование: разработать программу и провести анализ эффективности работы с использованием различных политик распараллеливания и с использованием автоматического параллелизатора, автоматического векторизатора.
Мой индивидуальный вариант – 17. Сформировать коллекцию L, включив в неё по одному разу элементы, которые входят в коллекцию L1, но не входят в коллекцию L2.
Математическая модель решения с указанием подходящих методов решения и алгоритмов из библиотеки STL
В качестве рабочего контейнера был выбран <vector>. Политиками execution выбраны sequenced_policy (seq), parallel_policy (par), parallel_unseqeunced_policy(par_unseq). В настройках компилятора периодически будут настраиваться автоматическая параллелизация и векторизация.
Будут созданы шаблоны STL:
Заполняющий контейнер случайными числами;
Итерация контейнера;
Шаблон, выполняющий ключевую функцию задачи – заполнение основного контейнера элементами обоих векторов; сортировка всех элементов в нем; удаление повторяющихся элементов и элементов, входящих во второй контейнер.
Задача функции main() заключается в исполнении вызова функции-шаблона, передавая в качестве параметров вектора. Также в этой функции будет выполняться подсчет времени выполнения программы.
Политика параллелизма может быть применена только для функций sort и for_each, которые присутствуют в составленной программе.
Для данной задачи подойдут следующие методы и алгоритмы из библиотеки STL:
unique() – для удаление равных соседних элементов при сохранении первого из них;
insert(p, x) – для добавления х перед элементом, на который указывает р;
end() – для указания на элемент, который следует за последним;
begin() – указывает на первый элемент;
find – для нахождения итератора на элемент и проверки на его существование;
count – для возвращения количества элементов с заданным значением;
erase – для удаление конкретного элемента.
remove – удаление элемента с определенным значением
for_each – применяет заданный функциональный объект к результату разыменования каждого итератора в диапазоне [first, last)
Сравнение эффективности выполнения
Числовые значения в ячейках фиксируют время с начала работы программы и измеряются в миллисекундах. Число элементов контейнера для заполнения равно одному миллиону.
-
Без авто параллелизации и авто векторизации
Vector 1
Vector 2
Vector
Без политики
2073
3965
10468
Политика “par”
1845
3701
9263
Политика “seq”
1980
3990
11169
Политика “par_unseq”
1895
3781
9422
С авто параллелизацией
Без политики
2132
4010
10777
Политика “par”
2134
4059
9566
Политика “seq”
2093
3859
10671
Политика “par_unseq”
1852
3635
9532
С авто векторизацией
Без политики
2027
4040
10744
Политика “par”
1939
3995
9173
Политика “seq”
2045
4272
10969
Политика “par_unseq”
1850
3627
9646
Таблица 1 – Скорость работы при разных настройках компилятора и политиках распараллеливания
Ссылаясь на табл. 1 можно сделать определённые выводы. Зеленым светом подсвечены значения, которые отличаются более быстрой скоростью работы. Скорость заполнения при всех политиках и при всех настройках компилятора примерно равны. Но при политиках “par” и “par_unseq” замечается рост скорости работы примерно на 1 секунду, что составляет сокращение времени работы на примерно 10%. Политика “seq” и без политичное распараллеливание показали худшие результаты. Особенно плохо показала себя политика “seq”, которая работает медленнее, чем при ситуации, когда политики нет. Стоит обратить внимание, что автоматическая параллелизация и векторизация немного, но ускоряют работы последних упомянутых политик.
Скриншоты работы
На рисунке 1 продемонстрирована работа компилятора с миллионом значений. Вызов функций «showme» закомментирован. Такой способ был использован для сравнения скорости работы при различных политиках распараллеливания и настройках компилятора.
Рисунок 1 – Пример работы программы с миллионом элементов
Рисунок 2 – Демонстрация работы программы с малым количеством элементов
Рисунок 3 – Настройки компилятора
Код программы
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <ctime>
#include <execution>
#include <thread>
using namespace std;
//заполение вектора
template <typename container>
int filling(container& con, int number)
{
int start = clock();
#pragma loop(hint_parallel(8));
for (int i = 0; i < number; i++)
{
int element = rand() % 1001;
con.insert(con.end(), element);
element = 0;
}
int end = clock();
return (end - start);
};
//отображение вектора
template <typename container>
void showme(container con)
{
cout << "Content of container:";
for_each(con.begin(), con.end(), [](auto a) {std::cout << a << " "; });
std::cout << "completed" << std::endl;
}
//выполнение задачи
template <typename container>
void del(container& con1, container& con2, container& con)
{
vector<int>::iterator it1;
for (it1 = con1.begin(); it1 != con1.end(); ++it1)
con.push_back(*it1);
//сортировка и удаление одинаковых элементов коллекции L для более легкого восприятия
sort(con.begin(), con.end());
auto last1 = unique(con.begin(), con.end());
con.erase(last1, con.end());
//уничтожение элементов L, которые входят в коллекцию L2
for (it1 = con2.begin(); it1 != con2.end(); ++it1)
{
con.erase(remove(con.begin(), con.end(), *it1), con.end());
}
}
int main()
{
int edro = std::thread::hardware_concurrency();
std::cout <<"There are " << edro << " concurrent supported threads.\n";
cout << "Enter number of values for container" << endl;
int number;
cin >> number;
vector<int> vect1;
vector<int> vect2;
vector<int> vect;
int time = 0;
time += filling(vect1, number);
showme(vect1);
cout << "Spent time for now: " << time << endl;
time += filling(vect2, number);
showme(vect2);
cout << "Spent time for now: " << time << endl;
int start = clock();
del(vect1, vect2, vect);
showme(vect);
int end = clock();
time += (end - start);
cout << "Spent time: " << time << endl;
cout << endl;
}
Вывод: Политики “par” и “par_unseq” ускорили работу программы, особенно при сортировке элементов при выполнении задачи, остальные не принесли ожидаемых результатов. В ходе работы я познакомился с такими понятиями, как распараллеливания кода, закрепил знания по алгоритмам STL. Также я выяснил, что параллельное выполнение потоков помогает ускорить работу программы за счет распределения ресурсов ядер процессора компьютера, что делает и так быстрый язык С++ более быстрым.
Список используемых ресурсов
https://pro.guap.ru/get-task/74c98e96d0cef0464bf8271a428c8a14
https://docs.microsoft.com/ru-ru/cpp/parallel/auto-parallelization-and-auto-vectorization?view=msvc-160#auto-vectorizer