2020-06-11 15:16:58 +10:00
|
|
|
|
# Лабораторная работа №11
|
|
|
|
|
|
|
|
|
|
## Задание 1
|
|
|
|
|
|
2020-06-18 12:24:32 +10:00
|
|
|
|
> vector-deadlock.c, main-common.c и др.:
|
|
|
|
|
> - Выполнить ./vector-deadlock -n 2 -l 1 -v
|
|
|
|
|
> которая инициирует 2 потока (-n 2) и каждый из них осуществляет одно сложение (-l 1) с опцией (-v).
|
|
|
|
|
> Объяснить результат. Меняется ли он от вызова к вызову?
|
|
|
|
|
> - Добавить флаг -d и изменить количество циклов -l .
|
|
|
|
|
> Всегда ли возникает состояние взаимной блокировки потоков (deadlock)?
|
|
|
|
|
> - Теперь меняем число потоков -n. Есть ли такое число потоков, при котором блокировка не возникает?
|
|
|
|
|
|
2020-06-11 15:16:58 +10:00
|
|
|
|
Программа печатает в каждом потоке начальные данные перед `vector_add` и результат выполнения данной функции.
|
|
|
|
|
Результат может меняться от вызова к вызову, так как весь worker не покрыт мьютексами, но на практике на такой небольшой
|
|
|
|
|
программе это маловероятно достижимо.
|
|
|
|
|
|
2020-06-18 12:24:32 +10:00
|
|
|
|
Обычно программа возвращает следующее:
|
|
|
|
|
```text
|
|
|
|
|
->add(0, 1)
|
|
|
|
|
<-add(0, 1)
|
|
|
|
|
->add(0, 1)
|
|
|
|
|
<-add(0, 1)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Спустя примерно 30 запусков, удалось получить другой результат:
|
|
|
|
|
```text
|
|
|
|
|
->add(0, 1)
|
|
|
|
|
->add(0, 1)
|
|
|
|
|
<-add(0, 1)
|
|
|
|
|
<-add(0, 1)
|
|
|
|
|
```
|
|
|
|
|
|
2020-06-11 15:16:58 +10:00
|
|
|
|
При добавлении `-d` взаимная блокировка возникает не всегда, а только при попадании переключения потоков между мьютексами.
|
|
|
|
|
|
2020-06-18 12:24:32 +10:00
|
|
|
|
Удалось достичь стабильный дедлок при запуске с ключами `-t -n 2 -l 100 -d` (без `-v`)
|
|
|
|
|
|
2020-06-11 15:16:58 +10:00
|
|
|
|
В случае, если число потоков = 1, взаимной блокировки не возникнет.
|
|
|
|
|
|
2020-06-18 12:24:32 +10:00
|
|
|
|
|
2020-06-11 15:16:58 +10:00
|
|
|
|
## Задание 2
|
|
|
|
|
|
2020-06-18 12:24:32 +10:00
|
|
|
|
> vector-global-order.c:
|
|
|
|
|
> - За счет чего программа избегает блокировок?
|
|
|
|
|
> - Для чего предусмотрен специальный случай в vector add(), когда исходный и результирующий вектор совпадают?
|
|
|
|
|
> - Флаги: -t -n 2 -l 100000 -d. Как меняется время в зависимости от числа циклов и числа потоков?
|
|
|
|
|
> - Что происходит, когда включается ключ -p (при сложении различных векторов и одного и того же)?
|
|
|
|
|
|
2020-06-11 15:16:58 +10:00
|
|
|
|
Программа избегает мёртвой блокировки за счёт упорядочивания по адресам, что позволяет постоянно сохранять порядок
|
|
|
|
|
блокировки.
|
|
|
|
|
|
|
|
|
|
В случае если адреса совпадают, то это один мьютекс, и для корректной работы программы его надо блокировать 1 раз.
|
|
|
|
|
|
|
|
|
|
В случае увеличения числа циклов и потоков, время выполнения растёт.
|
|
|
|
|
|
|
|
|
|
В случае включения `-p` время уменьшается, так как разрешается параллелизм.
|
|
|
|
|
|
|
|
|
|
## Задание 3
|
|
|
|
|
|
2020-06-18 12:24:32 +10:00
|
|
|
|
> vector-try-wait.c:
|
|
|
|
|
> - Нужен ли первый вызов pthread_mutex_trylock()?
|
|
|
|
|
> - Как меняется число повторных попыток, когда растет число потоков?
|
|
|
|
|
|
2020-06-11 15:16:58 +10:00
|
|
|
|
Вызовы pthread_mutex_trylock необходимы для создания порядка блокировки, для того чтобы избежать дедлока.
|
|
|
|
|
|
2020-06-18 12:24:32 +10:00
|
|
|
|
```text
|
|
|
|
|
$ ./vector-try-wait -t -n 2 -l 100 -d
|
|
|
|
|
Retries: 0
|
|
|
|
|
Time: 0.00 seconds
|
|
|
|
|
|
|
|
|
|
$ ./vector-try-wait -t -n 4 -l 100 -d
|
|
|
|
|
Retries: 847
|
|
|
|
|
Time: 0.00 seconds
|
|
|
|
|
```
|
|
|
|
|
|
2020-06-11 15:16:58 +10:00
|
|
|
|
С увеличением числа потоков происходит рост повторных попыток, что является логичным, так как переключение между потоками
|
|
|
|
|
становится более частым.
|
|
|
|
|
|
2020-06-18 12:24:32 +10:00
|
|
|
|
При использовании `-p` повторных попыток не возникает.
|
|
|
|
|
|
2020-06-11 15:16:58 +10:00
|
|
|
|
## Задание 4
|
|
|
|
|
|
2020-06-18 12:24:32 +10:00
|
|
|
|
> vector-avoid-hold-and-wait.c:
|
|
|
|
|
> - Сравнить с другими подходами.
|
|
|
|
|
> - Как меняется производительность в зависимости от наличия флага -p?
|
|
|
|
|
|
|
|
|
|
Данный подход защищает уязвимое место дедлока созданием глобального мьютекса, но при этом не даёт различным векторам
|
2020-06-11 15:16:58 +10:00
|
|
|
|
выполняться параллельно.
|
|
|
|
|
|
|
|
|
|
При использовании `-p` время уменьшается.
|
|
|
|
|
|
|
|
|
|
## Задание 5
|
|
|
|
|
|
2020-06-18 12:24:32 +10:00
|
|
|
|
> vector-nolock.c:
|
|
|
|
|
> - Сравнить семантику и производительность с другими вариантами при работе с двумя одинаковыми векторами и в случае,
|
|
|
|
|
> когда каждый поток работает на своем векторе -p.
|
|
|
|
|
|
|
|
|
|
Указав memory, программа дожидается завершения всех операцией с памятью, что позволяет заменить мьютексы в данном случае.
|
2020-06-11 15:16:58 +10:00
|
|
|
|
(https://ru.wikipedia.org/wiki/GCC_Inline_Assembly)
|
|
|
|
|
|
2020-06-18 12:24:32 +10:00
|
|
|
|
Фактически в программе производится атомарное сложение, и в стандарте C11 для этих целей есть особые типы:
|
|
|
|
|
(https://en.cppreference.com/w/c/language/atomic)
|
|
|
|
|
|
|
|
|
|
Также в C11 ввели поддержку потоков в стандартную библиотеку, что позволяет писать кроссплатформенный код:
|
|
|
|
|
(https://en.cppreference.com/w/c/thread)
|
|
|
|
|
|
|
|
|
|
Но атомарные операции очень дорого стоят.
|
2020-06-11 15:16:58 +10:00
|
|
|
|
Сравним время выполнения следующих команд:
|
|
|
|
|
|
|
|
|
|
```text
|
2020-06-18 12:24:32 +10:00
|
|
|
|
$ ./vector-nolock -t -n 2 -l 1000000 -d
|
|
|
|
|
Time: 7.20 seconds
|
|
|
|
|
|
|
|
|
|
$ ./vector-nolock -t -n 2 -l 1000000 -d -p
|
|
|
|
|
Time: 1.07 seconds
|
2020-06-11 15:16:58 +10:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```text
|
2020-06-18 12:24:32 +10:00
|
|
|
|
$ ./vector-avoid-hold-and-wait -t -n 2 -l 1000000 -d
|
|
|
|
|
Time: 4.46 seconds
|
|
|
|
|
|
|
|
|
|
$ ./vector-avoid-hold-and-wait -t -n 2 -l 1000000 -d -p
|
|
|
|
|
Time: 0.40 seconds
|
2020-06-11 15:16:58 +10:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```text
|
2020-06-18 12:24:32 +10:00
|
|
|
|
$ ./vector-try-wait -t -n 2 -l 1000000 -d
|
|
|
|
|
Retries: 5979033
|
|
|
|
|
Time: 2.55 seconds
|
|
|
|
|
|
|
|
|
|
$ ./vector-try-wait -t -n 2 -l 1000000 -d -p
|
|
|
|
|
Retries: 0
|
|
|
|
|
Time: 0.25 seconds
|
|
|
|
|
|
2020-06-11 15:16:58 +10:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```text
|
2020-06-18 12:24:32 +10:00
|
|
|
|
$ ./vector-global-order -t -n 2 -l 1000000 -d
|
|
|
|
|
Time: 1.23 seconds
|
|
|
|
|
|
|
|
|
|
$ ./vector-global-order -t -n 2 -l 1000000 -d -p
|
|
|
|
|
Time: 0.26 seconds
|
2020-06-11 15:16:58 +10:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Таким образом видно, что vector-nolock работает медленнее других в любом случае.
|