diff --git a/lab11/README.md b/lab11/README.md index 0aa20b5..9c194ca 100644 --- a/lab11/README.md +++ b/lab11/README.md @@ -2,16 +2,49 @@ ## Задание 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` и результат выполнения данной функции. Результат может меняться от вызова к вызову, так как весь 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` взаимная блокировка возникает не всегда, а только при попадании переключения потоков между мьютексами. +Удалось достичь стабильный дедлок при запуске с ключами `-t -n 2 -l 100 -d` (без `-v`) + В случае, если число потоков = 1, взаимной блокировки не возникнет. + ## Задание 2 +> vector-global-order.c: +> - За счет чего программа избегает блокировок? +> - Для чего предусмотрен специальный случай в vector add(), когда исходный и результирующий вектор совпадают? +> - Флаги: -t -n 2 -l 100000 -d. Как меняется время в зависимости от числа циклов и числа потоков? +> - Что происходит, когда включается ключ -p (при сложении различных векторов и одного и того же)? + Программа избегает мёртвой блокировки за счёт упорядочивания по адресам, что позволяет постоянно сохранять порядок блокировки. @@ -23,43 +56,89 @@ ## Задание 3 +> vector-try-wait.c: +> - Нужен ли первый вызов 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 -Данный подход защищает уязвимое место дедлока, созданием глобального мьютекса, но при этом не даёт различным векторам +> vector-avoid-hold-and-wait.c: +> - Сравнить с другими подходами. +> - Как меняется производительность в зависимости от наличия флага -p? + +Данный подход защищает уязвимое место дедлока созданием глобального мьютекса, но при этом не даёт различным векторам выполняться параллельно. При использовании `-p` время уменьшается. ## Задание 5 -Указав memory, мы дожидаемся завершения всех операцией с памятью, что своего рода позволяет заменить мьютексы. +> vector-nolock.c: +> - Сравнить семантику и производительность с другими вариантами при работе с двумя одинаковыми векторами и в случае, +> когда каждый поток работает на своем векторе -p. + +Указав memory, программа дожидается завершения всех операцией с памятью, что позволяет заменить мьютексы в данном случае. (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 -./vector-nolock -t -n 2 -l 1000000 -d = 4.08 -./vector-nolock -t -n 2 -l 1000000 -d -p = 0.65 +$ ./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 ``` ```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 -p = 0.45 +$ ./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 ``` ```text -./vector-try-wait -t -n 2 -l 1000000 -d = 1.30 -./vector-try-wait -t -n 2 -l 1000000 -d -p = 0.18 +$ ./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 + ``` ```text -./vector-global-order -t -n 2 -l 1000000 -d = 0.69 -./vector-global-order -t -n 2 -l 1000000 -d -p = 0.19 +$ ./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 ``` Таким образом видно, что vector-nolock работает медленнее других в любом случае.