Журнал Компьютерра - Журнал «Компьютерра» №44 от 29 ноября 2005 года
- Название:Журнал «Компьютерра» №44 от 29 ноября 2005 года
- Автор:
- Жанр:
- Издательство:неизвестно
- Год:неизвестен
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Журнал Компьютерра - Журнал «Компьютерра» №44 от 29 ноября 2005 года краткое содержание
Журнал «Компьютерра» №44 от 29 ноября 2005 года - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
А теперь представим, что все то же самое мы хотим сделать «параллельно».
Зачем? Ну хотя бы затем, что сегодня это модно. И без технологии HyperThreading и оптимизаций под нее нам уже года три как не жить. На самом деле, конечно, причина грядущего перехода к параллельному программированию в том, что крупнейший производитель процессоров для ПК - корпорация Intel - обещает, что к концу следующего года более 70% продаваемых ею процессоров[А стало быть, как минимум половина всех продаваемых x86-совместимых процессоров] будут двухъядерными, - с чуть меньшей производительностью в пересчете на один-единственный поток исполнения, но зато выполняющие два (или даже четыре) потока одновременно[Подробнее о двухъядерных процессорах см. на offline.computerra.ru/2005/594/39218]. Поэтому если программист сумеет хорошо «раскидать» программу на два параллельных потока (сам процессор делать этого не умеет), то он получит на двухъядерном чипе гораздо большую производительность, чем на аналогичном по стоимости одноядерном. А если не сумеет - то получится как в хорошо знакомом всем россиянам изречении о том, что «хотели как лучше…». Таким образом, налицо и кнут (грозящее снижение производительности, если оставить все «как есть»), и пряник (потенциальный прорыв в скорости) - более чем убедительные аргументы за то, чтобы не отставать от технического прогресса.

Но как это вообще делается? В классическом варианте - полностью вручную. Главный поток программы (который создала при запуске приложения операционная система) формирует (посредством специальных системных вызовов) несколько новых потоков[В случае Unix-систем при этом происходит весьма нетривиальная вещь: при создании первого потока «главный» поток как бы «замораживается» операционной системой, а в операционной системе возникают еще две сущности - новый поток, запущенный по просьбе «главного», и «наследующий» поток, который продолжает исполнение «главного» кода, но не является собственно процессом], приступающих к выполнению программы с того места, которое указывается в числе параметров вызова. Детали реализации в разных ОС отличаются[Существует два основных стандарта: используемый в мире Open Source стандартный интерфейс pthreads (POSIX Threads) и детище Microsoft - так называемая Win32 Threading model], однако принцип совершенно одинаков: одна программа, одни и те же данные, несколько точек исполнения, одновременно перемещающихся по программе. Таким образом, вместо кода типа
Выполнить Действие1( )
Выполнить Действие2( )
мы записываем что-то вроде
ЗапуститьПоток(Действие1)
ЗапуститьПоток(Действие2)
и при этом Действие1 и Действие2 выполняются параллельно и независимо друг от друга. То есть в отличие от «классики», где программа сперва проверяет, попал ли в танк снаряд, а уж затем решает, что этому танку делать дальше, здесь обсчет поведения объектов происходит одновременно. Правда, поскольку действие, как правило, выполняется одно, но над разными данными (скажем, для десятка танков вызывается один и тот же программный код, рассчитывающий физику и новые координаты танка), то гораздо чаще возникает код
ЗапуститьПоток(Действие, для Объекта1)
ЗапуститьПоток(Действие, для Объекта2)
где в самом действии образуется конструкция вида
Понять, для каких данных нужно выполнять действие
Выполнить действие для этих данных
С практической точки зрения это означает, что теперь не только снаряды летают одновременно с перемещением танков, но и танки ездят не «по очереди», а все сразу. И поскольку танков и снарядов у нас довольно много, то, казалось бы, игра не просто параллелится, - она разбивается на сотни потоков и, стало быть, сможет получить дополнительные преимущества даже на будущих двухпроцессорных 32-ядерных системах с поддержкой четырехпоточного HyperThreading. Однако этого не происходит, и вот почему: запуск потока - весьма и весьма дорогая по меркам процессора процедура, которая требует немалого времени, грозящего свести на нет все преимущества параллельной обработки. Переключение между потоками - тоже процесс небыстрый, и если мы разбили исполнение программы на 32 потока, а процессор умеет исполнять только два потока одновременно, то постоянные переключения между шестнадцатью потоками на каждое виртуальное ядро очень сильно «просадят» производительность. А потому программисты зачастую отказываются от «простого» решения и прибегают к более сложной конструкции, когда все необходимые рабочие потоки (причем их число тщательно выбирается, чтобы исключить лишние переключения) запускаются заблаговременно, а в нужных местах «главный» поток «раздает» им текущие задания. Что-то вроде
ЗапуститьПоток(Поток1)
ЗапуститьПоток(Поток2)
ПопроситьПотокСделать(Поток1, Действие, для Объекта1)
ПопроситьПотокСделать(Поток2, Действие, для Объекта2)
В результате программист уже на начальном этапе вынужден возиться с довольно громоздкими и сложными конструкциями, которые далеко не так просто написать и отладить. И даже на этой первой, самой простой проблеме параллельного программирования многие спотыкаются. Чтобы облегчить жизнь новичкам и облегчить знакомство с параллельным кодом, существуют проекты типа OpenMP.
Первая спецификация компилятора OpenMP (Open specifications for Multi-Processing), являющегося развитием провального и ныне забытого проекта ANSI X3H5, появилась в 1997 году и предназначалась для одного из древнейших языков программирования Fortran, на котором некогда было написано большинство «серьезных» вычислительных приложений. В 1998 году появились варианты OpenMP для языков C/C++; стандарт прижился, получил распространение и к настоящему моменту дорос до версии 2.5. Поддержка спецификации есть во всех компиляторах Intel, начиная с шестой версии (OpenMP 2.0 - с восьмой); в компиляторе Microsoft C/C++, начиная с Visual Studio 2005; буквально на днях стало известно о худо-бедно стандартизованном OpenMP-расширении для GCC[OpenMP для GNU-систем, разумеется, существовал и раньше. Но проект GOMP (GNU OpenMP), обеспечивающий полноценное встраивание поддержки OpenMP непосредственно в GCC, появился только сейчас. 18 ноября пришло сообщение о готовности встроить GOMP в свежие билды GCC - ждем с нетерпением! Для линуксоидов, конечно, вручную параллелить код для pthreads - дело привычное, однако полноценная поддержка OpenMP со стороны GNU Project полностью устранит проблему портирования параллельных приложений между ОС, использующими разные модели потоков].
OpenMP идеально портируется. Он не привязывается к особенностям операционной системы и позволяет создавать переносимые приложения, использующие потоки и объекты синхронизации. Вдобавок большинство OpenMP-директив являются (в терминологии С/C++) «прагмами» (#pragma), а потому попросту игнорируются не понимающим их компилятором[Кстати, программисты, учтите: поддержку OpenMP зачастую требуется явно включать ключом в компиляторе! И еще: далеко не все возможности OpenMP сводятся к прагмам], который генерирует из OpenMP-программ вполне корректные, хотя и однопоточные приложения.
Читать дальшеИнтервал:
Закладка: