Мы применили редукцию и попутно исключили ситуацию гонки.
Рис. 4.14. Параллельный запуск
Задание. Запустите параллельную программу с разным количеством потоков и изучите полученные результаты.
4.2.3. Порядок выполнения потоков
Следующий эксперимент изучим порядок запуска и выполнения параллельных потоков.
Составим программу на основе предыдущих (рис. 4.15). Обратите внимание, что мы временно убрали редукцию из директивы parallel. Все потоки обращаются к одной и той же общей переменной S.
Программа печатает заголовок таблицы (строка 6).
Затем выводит номер потока и значение суммы (строка 10).
Так мы сможем проследить, в каком порядке работает программа.
Рис. 4.15. Порядок потоков
Запускаем программу, получаем таблицу (рис. 4.16).
Как видим, номера потоков печаются в случайном порядке.
Рис. 4.16. Порядок потоков
Задание. Создайте программу (рис. 4.15) и запустите её. Обратите внимание на порядок выполнения потоков.
Перейдём в командное окно.
Запускаем нашу программу и направим вывод в файл вместо вывода на экран:
omp-10-order> order. txt.
Эта манипуляция называется «перенаправление стандартного вывода». Экран это стандартное устройство вывода, с которым работает функция printf. Вместо экрана, весь текстовый вывод отправляется в файл.
Итак, теперь вся таблица вместе с заголовком записана в текстовый файл. Открываем файл в Блокноте и убеждаемся в том, что вся информация здесь (рис. 4.17).
Рис. 4.17. Вывод в файл
Задание. Запустите программу и направьте вывод в файл. Откройте файл в Блокноте и изучите его содержимое.
Загрузим текстовый файл для анализа в электронную таблицу.
Запускаем Excel. Вызываем в верхнем меню открытие файла:
File Open Other locations Browse.
Переходим в каталог Release и находим текстовый файл: order. txt.
Устанавливаем загрузку текстовых файлов:
Text Files (*prn, *.txt, *csv).
В правой части окна видим содержимое файла (рис. 4.18).
Нажимаем ОК.
Рис. 4.18. Загрузка файла
В окне Мастера импорта файла настраиваем параметры:
Original data type Delimited;
My data has headers (рис. 4.19).
Нажимаем Next.
Рис. 4.19. Первый шаг импорта
На втором шаге импорта указываем разделитель полей символ табуляции:
Delimiters Tab.
Вспоминаем, что при выводе на экран мы использовали табуляцию.
Нажимаем Next (рис. 4.20).
Рис. 4.20. Второй шаг импорта
После импорта файла получаем таблицу в Excel (рис. 4.21).
Выделяем диапазон ячеек заголовок и данные.
Выбираем в верхнем меню форматирование таблицы:
Home Styles Format as Table.
Выбираем оформление таблицы.
Рис. 4.21. Таблица из файла
Задание. Загрузите текстовый файл в Excel.
Сортируем таблицу по колонке S (рис. 4.22).
Для этого нажимаем кнопку в виде стрелочки справа от названия столбца. Выбираем в выпадающем списке сортировку по возрастанию:
Sort Smallest to Largest.
Изучаем порядок выполнения потоков.
Наблюдаем совершенно непредсказуемую картину.
Рис. 4.22. Сортировка по S
Задание. Выполните сортировку и изучите порядок выполнения потоков.
4.2.4. Локальные копии переменной
Вернёмся к редукции и рассмотрим локальные копии общей переменной.
Итак, в директиве parallel мы организуем редукцию переменной S путём сложения частичных, промежуточных значений:
Итак, в директиве parallel мы организуем редукцию переменной S путём сложения частичных, промежуточных значений:
reduction (+:S).
Это означает, что при входе в параллельную область создаётся несколько параллельных потоков. В каждом из этих потоков (включая и главный поток) берётся наша общая переменная S и для неё создаётся локальная копия. То есть в каждом потоке появляется своя внутренняя переменная.
Мало того, в момент создания локальная копия ещё и получает начальное значение. Это называется ИНИЦИАЛИЗАЦИЯ «присвоение начального значения». Если редукция делается путём суммирования, то локальные копии получают нулевое начальное значение.
Немного изменим предыдущею программу (рис. 4.23).
Перед началом параллельной области присвоим общей переменной S начальное значение 10 (строка 5).
Добавляем редукцию для параллельной области (строка 7).
В цикле делаем 20 итераций, чтобы более наглядно показать процесс выполнения программы (строка 8).
После завершения параллельной области и редукции выводим собранное из локальных копий суммарное значение общей переменной S (строка 12).
На экран дополнительно выводим столбец значений счётчика цикла i (строки 6, 10, 12).
После завершения цикла в колонке значений счётчика выводим число 20 (строка 12). Обратим внимание, что при выполнении цикла счётчик принимает значения от 0 до 19.
Рис. 4.23. Отслеживание инициализации
Далее проделаем знакомые нам шаги по выполнению программы и анализу результатов в Excel.
Компилируем программу и убеждаемся в отсутствии ошибок.
Переходим в командное окно.
Задаём три потока:
set OMP_NUM_THREADS=3.
Запускаем программу на выполнение.
Перенаправляем вывод в текстовый файл.
Загружаем файл в Excel.
Форматируем таблицу и сортируем по номеру потока (рис. 4.24).
Теперь можно немного отдышаться и рассмотреть результаты работы.
При входе в параллельную область каждая локальная копия переменной S получила нулевое значение. На каждой итерации цикла к ней прибавляли по единичке.