Update 11th lab

master
Yury Kurlykov 2020-06-18 12:24:32 +10:00
parent be91d8d06c
commit 1243883cec
Signed by: t1meshift
GPG Key ID: B133F3167ABF94D8
1 changed files with 89 additions and 10 deletions

View File

@ -2,16 +2,49 @@
## Задание 1 ## Задание 1
> vector-deadlock.c, main-common.c и др.:
> - Выполнить ./vector-deadlock -n 2 -l 1 -v
> которая инициирует 2 потока (-n 2) и каждый из них осуществляет одно сложение (-l 1) с опцией (-v).
> Объяснить результат. Меняется ли он от вызова к вызову?
> - Добавить флаг -d и изменить количество циклов -l .
> Всегда ли возникает состояние взаимной блокировки потоков (deadlock)?
> - Теперь меняем число потоков -n. Есть ли такое число потоков, при котором блокировка не возникает?
Программа печатает в каждом потоке начальные данные перед `vector_add` и результат выполнения данной функции. Программа печатает в каждом потоке начальные данные перед `vector_add` и результат выполнения данной функции.
Результат может меняться от вызова к вызову, так как весь worker не покрыт мьютексами, но на практике на такой небольшой Результат может меняться от вызова к вызову, так как весь worker не покрыт мьютексами, но на практике на такой небольшой
программе это маловероятно достижимо. программе это маловероятно достижимо.
Обычно программа возвращает следующее:
```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)
```
При добавлении `-d` взаимная блокировка возникает не всегда, а только при попадании переключения потоков между мьютексами. При добавлении `-d` взаимная блокировка возникает не всегда, а только при попадании переключения потоков между мьютексами.
Удалось достичь стабильный дедлок при запуске с ключами `-t -n 2 -l 100 -d` (без `-v`)
В случае, если число потоков = 1, взаимной блокировки не возникнет. В случае, если число потоков = 1, взаимной блокировки не возникнет.
## Задание 2 ## Задание 2
> vector-global-order.c:
> - За счет чего программа избегает блокировок?
> - Для чего предусмотрен специальный случай в vector add(), когда исходный и результирующий вектор совпадают?
> - Флаги: -t -n 2 -l 100000 -d. Как меняется время в зависимости от числа циклов и числа потоков?
> - Что происходит, когда включается ключ -p (при сложении различных векторов и одного и того же)?
Программа избегает мёртвой блокировки за счёт упорядочивания по адресам, что позволяет постоянно сохранять порядок Программа избегает мёртвой блокировки за счёт упорядочивания по адресам, что позволяет постоянно сохранять порядок
блокировки. блокировки.
@ -23,43 +56,89 @@
## Задание 3 ## Задание 3
> vector-try-wait.c:
> - Нужен ли первый вызов pthread_mutex_trylock()?
> - Как меняется число повторных попыток, когда растет число потоков?
Вызовы pthread_mutex_trylock необходимы для создания порядка блокировки, для того чтобы избежать дедлока. Вызовы pthread_mutex_trylock необходимы для создания порядка блокировки, для того чтобы избежать дедлока.
```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
```
С увеличением числа потоков происходит рост повторных попыток, что является логичным, так как переключение между потоками С увеличением числа потоков происходит рост повторных попыток, что является логичным, так как переключение между потоками
становится более частым. становится более частым.
При использовании `-p` повторных попыток не возникает.
## Задание 4 ## Задание 4
Данный подход защищает уязвимое место дедлока, созданием глобального мьютекса, но при этом не даёт различным векторам > vector-avoid-hold-and-wait.c:
> - Сравнить с другими подходами.
> - Как меняется производительность в зависимости от наличия флага -p?
Данный подход защищает уязвимое место дедлока созданием глобального мьютекса, но при этом не даёт различным векторам
выполняться параллельно. выполняться параллельно.
При использовании `-p` время уменьшается. При использовании `-p` время уменьшается.
## Задание 5 ## Задание 5
Указав memory, мы дожидаемся завершения всех операцией с памятью, что своего рода позволяет заменить мьютексы. > vector-nolock.c:
> - Сравнить семантику и производительность с другими вариантами при работе с двумя одинаковыми векторами и в случае,
> когда каждый поток работает на своем векторе -p.
Указав memory, программа дожидается завершения всех операцией с памятью, что позволяет заменить мьютексы в данном случае.
(https://ru.wikipedia.org/wiki/GCC_Inline_Assembly) (https://ru.wikipedia.org/wiki/GCC_Inline_Assembly)
Фактически в программе производится атомарное сложение, и в стандарте C11 для этих целей есть особые типы:
(https://en.cppreference.com/w/c/language/atomic)
Также в C11 ввели поддержку потоков в стандартную библиотеку, что позволяет писать кроссплатформенный код:
(https://en.cppreference.com/w/c/thread)
Но атомарные операции очень дорого стоят.
Сравним время выполнения следующих команд: Сравним время выполнения следующих команд:
```text ```text
./vector-nolock -t -n 2 -l 1000000 -d = 4.08 $ ./vector-nolock -t -n 2 -l 1000000 -d
./vector-nolock -t -n 2 -l 1000000 -d -p = 0.65 Time: 7.20 seconds
$ ./vector-nolock -t -n 2 -l 1000000 -d -p
Time: 1.07 seconds
``` ```
```text ```text
./vector-avoid-hold-and-wait -t -n 2 -l 1000000 -d = 2.98 $ ./vector-avoid-hold-and-wait -t -n 2 -l 1000000 -d
./vector-avoid-hold-and-wait -t -n 2 -l 1000000 -d -p = 0.45 Time: 4.46 seconds
$ ./vector-avoid-hold-and-wait -t -n 2 -l 1000000 -d -p
Time: 0.40 seconds
``` ```
```text ```text
./vector-try-wait -t -n 2 -l 1000000 -d = 1.30 $ ./vector-try-wait -t -n 2 -l 1000000 -d
./vector-try-wait -t -n 2 -l 1000000 -d -p = 0.18 Retries: 5979033
Time: 2.55 seconds
$ ./vector-try-wait -t -n 2 -l 1000000 -d -p
Retries: 0
Time: 0.25 seconds
``` ```
```text ```text
./vector-global-order -t -n 2 -l 1000000 -d = 0.69 $ ./vector-global-order -t -n 2 -l 1000000 -d
./vector-global-order -t -n 2 -l 1000000 -d -p = 0.19 Time: 1.23 seconds
$ ./vector-global-order -t -n 2 -l 1000000 -d -p
Time: 0.26 seconds
``` ```
Таким образом видно, что vector-nolock работает медленнее других в любом случае. Таким образом видно, что vector-nolock работает медленнее других в любом случае.