воскресенье, 24 февраля 2008 г.

Правила параллельного программирования для многоядерных систем

Программирование для многоядерных процессоров делает необходимым поиск решений для абсолютно новых невостребованных ранее задач. Большинство существующих практик проектирования и разработки программного обеспечения не в состоянии максимально эффективно использовать всю потенциальную мощность новых многоядерных систем. Нынешнее программное обеспечение реализовано, как правило, для выполнения на одноядерных платформах и поэтому не в состоянии эффективно использовать несколько ядер. К тому же, использование в подобных программах фиктивного распараллеливания вычислительных процессов еще более усугубляют имеющиеся проблемы эффективности выполнения «одноядерных» приложений на процессорах, имеющих несколько ядер.
Джеймс Рейндерс, автор книги «Intel Threading Building Blocks: Outfitting C++ for Multi-core Processor Parallelism» сформулировал восемь ключевых правил программирования для многоядерных процессоров, которые окажутся очень кстати на пути к увеличению эффективности ваших программ.

Правило 1.
Думайте параллельно. Решение всех проблем ищите в параллелизме. Обретите понимание того, где действительно есть параллелизм и организуйте свое мышление таким образом, что бы смогли без труда выявить такие фрагменты системы. Прежде чем использовать другие приемы и реализации, склоняйтесь к максимально эффективному решению, основанному на параллельном видении проблемы. Учитесь «думать параллельно».

Правило 2.
Программируйте, используя абстракции. При написании исходного кода сфокусируйтесь на параллельности выполнения процессов, но избегайте непосредственного управления потоками или ядрами процессора. Такие библиотеки, как OpenMP и Intel Threading Building Blocks это все примеры использования абстракций. Не используйте объекты потоков напрямую (pthreads, Windows Threads, Boost threads и пр.) Потоки и MPI это механизмы параллелизма языков программирования. Они предлагают максимум гибкости, однако требуют чрезвычайно много времени для написания, отладки и поддержания. Ваше внимание должно быть сосредоточено на более высоком уровне видения решаемой проблемы, чем уровень управления ядрами и потоками.

Правило 3.
Программируйте в терминах задач, а не потоков вычисления. Оставьте вопрос привязывания задач к потокам или ядрам процессоров как отдельную изолированную операцию в вашей программе, абстрагируя механизм управления вычислительными потоками. Создавайте максимально обособленные реализации решаемых в вашей программе задач, которые потенциально могут автоматически выполняться на различных ядрах процессора (как, к примеру, цикл OpenMP). В процессе формулирования реализуемых в программе отдельных подзадач, создавайте их ровно столько, сколько вам действительно нужно, избегая, разумеется, избыточного разделения.

Правило 4.
Проектируйте вашу систему таким образом, чтобы была возможность не использовать параллелизм вовсе. Для того, что бы проще выполнять отладку, создавайте программу, которую можно было бы запустить без параллельного выполнения задач. В таком случае у вас будет возможность при отладке программы сначала запустить ее с параллельными вычислениями, а затем без таковых и проверить результаты обоих запусков на наличие ошибок и слабых мест. Отладка отдельных частей программы проще, когда в ней нет параллельно работающих фрагментов. Это связано с тем, что основная масса существующих инструментов отладки в большей степени не ориентированы на работу с многопоточным кодом. Когда вам известно, что проблемы возникают только в том случае, когда части системы выполняются параллельно, то наверняка выявить причину этого вам будет проще. Если проигнорировать данное правило и не предусмотреть возможности работы программы в однопоточном режиме, вы можете потратить очень много вермени на выявление причины существующих в программе ошибок. После этого у вас наверняка появится желание добавить в программу возможность ее выполнения в однопоточном режиме, но это уже не принесет такой эффективности, как если бы такая возможность присутствовала изначально. Вам просто необходимо избегать создание параллельных программ, которые для работы обязательно требуют параллельности выполнения. MPI программы часто нарушают это правило, что отчасти является причиной того, что подобные программы сложно отладить.

Правило 5.
Избегайте использования блокировок (lock). Просто скажите блокировкам «нет». Блокировки замедляют программы, снижают их масштабируемость и являются источниками ошибок в параллельном программировании. Делайте неявную синхронизацию решения для вашей программы. Когда вам все-таки требуется явная синхронизация, используйте атомарные операции. Используйте блокировки только как последнее средство. Тщательно спроектируйте вашу систему таким образом, что бы использование блокировок в вашей программе не понадобилось.

Правило 6.
Используйте средства и библиотеки, предназначенные для того, что бы помочь реализации параллельных вычислений. Даже не прикасайтесь к устаревшему инструментарию. Будьте критичны к тому, поддерживает ли данное средство параллелизм и если да, то насколько хорошо. Большинство существующих подобных инструментов достаточно слабы в этом вопросе и не полностью поддерживают параллельные процессы. Ищите потокобезопасные библиотеки или же такие, которые спроектированы специально для применения в многопоточной среде.

Правило 7.
Используйте масштабируемые механизмы выделения памяти. Поточные программы требуют использования масштабируемых механизмов выделения памяти. Точка. Существует много решений, которые лучше чем malloc(). Использование таких механимов увеличивает скорость работы программы, устраняя узкие места, связанные с перераспределением памяти и способствуют лучшему использованию системного кеша.

Правило 8.
Проектируйте масштабируемость программы с учетом увеличения нагрузки. Со временем количество выполняемой вашей программой работы, возможно, потребуется увеличить. Учтите это на этапе проектирования. Уделите большое внимание вопросам масшабируемости еще на начальном этапе, и ваша программа сможет делать больше работы при увеличении числа ядер процессора. Каждый год мы вынуждаем компьютеры делать все больше и больше работы. Затратив больше времени вопросам масштабируемости на начальном этапе, вы получите огромную выгоду при возрастании нагрузок в будущем.

Источник: Dr. Dobb's