Введение
Важную роль в создании многопоточных приложений для поддержки многоядерных платформ Intel играет правильный выбор программных библиотек.
Многоядерные платформы уже стали стандартом для продаваемых во всем мире компьютеров. Такие платформы содержат несколько процессоров, реализованных на одном микрочипе. Иными словами, микрочип имеет несколько ядер, каждое из которых работает как полноценный процессор. Десять лет назад многопроцессорные конфигурации были доступны только в сегментах серверов и высокопроизводительных рабочих станций, которые имели симметричную многопроцессорную архитектуру (SMP, symmetric multi-processor). Сегодня многоядерные платформы предлагают те же преимущества, что и системы SMP. В настоящее время для серверов и высокопроизводительных рабочих станция выпускаются системы SMP с несколькими многоядерными процессорами. Многоядерные процессоры устанавливаются даже в ноутбуки. Каждое ядро или процессор на многоядерном чипе может выполнять различные вычислительные задачи независимо, тем самым повышая производительность всей системы и эффективное решение задач пользователя.
Для реализации многопоточности в приложении команде разработчиков нередко приходится прилагать значительные усилия. Библиотеки, уже имеющие поддержку нескольких потоков, представляют собой экономичный способ оптимизации приложений, который позволяет задействовать весь потенциал производительности многоядерных платформ. В данной статье рассматривается преимущества использования библиотек в процессе оптимизации приложений к условиям многоядерной среды.
В целях эффективной работы на нескольких ядрах, приложение должно распределять задачи таким образом, чтобы задействовать каждое ядро. Самым распространенным механизмом разделения задач для одновременного выполнения в рамках отдельного приложения является многопоточность. Потоки представляют собой части одного процесса и могут использовать общие данные. Каждой задаче соответствует определенный поток, который назначается операционной системой одному из ядер процессора. Серверное программное обеспечение для предприятий уже давно поддерживает потоки для повышения производительности и хорошо работает на многоядерных процессорах.
Большинство приложений для настольных систем поддерживает потоки для удобства, то есть в отдельные потоки выделяются задачи, не требующие большого объема вычислений (например, ввод-вывод). При выполнении таких приложений на многоядерной платформе прирост производительности, как правило, незначителен. Если же в отдельные потоки выделяются задачи с интенсивной вычислительной нагрузкой, то и они смогут выполняться независимо и одновременно, что приведет к повышению общей производительности приложения. Распределение задач с большим объемом вычислений между несколькими ядрами дает ощутимые преимущества для пользователя:
- приложение может выполняться быстрее и выполнить тот же объем работы за меньшее время;
- приложение может выполнить больше вычислений в единицу времени, что может привести, например, к улучшению визуальных или звуковых эффектов.
Библиотеки и разделение потоков
Библиотеки – удобный инструмент разработки программного обеспечения. Независимо от того, созданы ли они самостоятельно или лицензированы у третьих сторон, библиотеки могут играть важную роль при написании программного кода, а также влиять на производительность и реализацию потоков. С помощью библиотек можно использовать уже написанный код, что позволяет экономить средства на разработку и развитие ПО. Один раз оптимизировав библиотеку с точки зрения производительности, можно увеличить производительность целого ряда приложений. Познакомившись с библиотекой, разработчики могут успешно применять ее в различных проектах. При выборе библиотеки для использования в составе продукта необходимо учитывать целый ряд факторов. В данной статье мы рассмотрим те из них, которые касаются многопоточности.
Безопасность потоков
Библиотека может быть безопасной для потоков (thread-safe), поддерживающей многопоточность для улучшения производительности, либо одновременно безопасной для потоков и поддерживающей многопоточность для улучшения производительности. Мы постараемся объяснить, что подразумевается под этими терминами, рассмотрев каждый из них в отдельности.
Если библиотека является безопасной для потоков, различные потоки приложения могут одновременно вызывать функции библиотеки. Наиболее часто встречающаяся трудность при создании безопасной для потоков библиотеке – это обработка статических и глобальных переменных. Если сама библиотека или ее функции содержат статические переменные, которые считываются и обновляются сразу несколькими потоками, это может привести к непредсказуемому поведению программы (undefined behavior). Аналогичная ситуация применима и к глобальным переменным, как внешним, так и внутренним по отношению к библиотеке. Библиотеку можно сделать безопасной для потоков, добавив мьютекс-объекты (mutexes) или критические секции кода, обращающиеся к статическим или глобальным переменным. Тем не менее, в интересах производительности количество мьютексов или критических секций в коде лучше свести к минимуму, даже если они необходимы. По возможности, рекомендуется создавать локальную копию данных и передавать ее библиотеке в качестве параметра. Когда каждый поток работает с отдельной локальной копией, отсутствует риск конкурентного доступа потоков к данным (data race conditions) по мере выполнения задач.
Многопоточные библиотеки и повышение производительности
Если библиотека поддерживает многопоточность, это означает, что она может разбивать задачи на подзадачи и назначать их различным потокам. В целях безопасного управления такими подзадачами может потребоваться применить мьютекс-объекты или критические секции. Обычно подразумевается, что библиотека создает собственный набор потоков для вычислений. В то же время, можно реализовать общий для библиотеки и основного приложения пул потоков, однако, это потребует более тщательного подхода к написанию программного кода. Если библиотека и основное приложение используют общий пул потоков, эффективность повышается. Нам встречались примеры, когда в своем приложении разработчики создавали потоки для каждой библиотеки, и в итоге создавалось более 80 потоков, из которых в любой момент времени было активно только 4. Общий пул потоков позволяет избежать неоправданного расхода ресурсов.
Библиотеки с поддержкой многопоточности и требования безопасности потоков
Библиотека с поддержкой многопоточности может быть как безопасной для потоков, так и нет. Если библиотека поддерживает потоки и является безопасной для потоков, различные потоки могут одновременно вызывать эту библиотеку и, в то же время, библиотека может порождать дополнительные потоки, которые будут выполняться параллельно. Данная схема называется вложенной многопоточностью (nested threading). Если библиотека поддерживает потоки, но не является безопасной для потоков, только один поток может обращаться к ней в единицу времени. В противном случае это может привести к неопределенному поведению программы и возвращению недетерминированных результатов (т.е. функция может возвращать разные результаты при разных вызовах с одним и тем же набором аргументов). Рекомендуется предварительно получить у поставщика или разработчика библиотеки необходимую документацию по ее применению в многопоточной среде. Кроме того, безопасность библиотеки для потоков можно проверить с помощью инструмента Intel® Thread Checker.
Сама библиотека или ее функции могут быть безопасными для потоков, но при этом зависеть от переданных им указателей переменных (pointers). Например, функция stringcopy в исполняемой библиотеке может быть безопасной для потоков, и различные потоки могут одновременно вызывать stringcopy без каких-либо сбоев. Однако, если вы делаете два вызова stringcopy двумя различными потоками и обоим вызовам передается один и тот же адрес, вы можете получить недерминированный результат – не потому, что функция не является безопасной для потоков, а потому что она была вызвана некорректно.
Примеры использования многопоточных библиотек
Библиотеки Intel® Math Kernel Libraries (MKL) являются многопоточными и безопасными для потоков. На Рис.1 показаны возможности масштабирования MKL по мере увеличения числа потоков с 1 до 8. Библиотека MKL включает несколько различных API для выполнения процедуры быстрых преобразований Фурье в трехмерной системе (3D FFT), что облегчает ее применение разработчиками. На приведенной ниже схеме показано, что библиотека уже оптимизирована, следовательно, она способна улучшить производительность аналитического кода, оперирующего 3D FFT.

Создание многопоточной или безопасной для потоков библиотеки
Разработчикам, которые планируют сами создать собственную многопоточную или безопасную для потоков библиотеку, следует ознакомиться с нашими рекомендациями по разработке многопоточного программного обеспечения, которые упоминались в других работах и представлены на Рисунке xx. В процессе разработки мы выделяем 4 этапа:
- Анализ
- Выбор и реализация
- Проверка правильности
- Оптимизация
Перед тем, как переходить к обсуждению собственно потоков и библиотек, мы вкратце рассмотрим каждый из перечисленных этапов.
Анализ: Первым этапом является анализ. Если вы начинаете новый проект, предметом анализа может стать общая схема проекта или модель на языке UML. Вам также потребуется уточнить последовательность действий и обмена данными. На данном этапе следует изучить возможности распараллеливания: какие задачи могут выполняться в параллельном режиме, а какие могут быть разбиты на меньшие блоки для одновременной обработки несколькими потоками. Предусматривайте механизмы распараллеливания в архитектуре приложения с самого начала. Это лучший способ получить в итоге эффективный масштабируемый код. Если вы работаете со стандартным кодом, что встречается довольно часто, важно вначале разобраться в особенностях его выполнения. Даже если вы считаете, что понимаете его принципы, будет полезно проверить ваши предположения с помощью фактических данных. Мы рекомендуем воспользоваться инструментом VTune™ Performance Analyzer для выявления «узких мест» в производительности и сбора данных для оценки потенциальной выгоды от реализации многопоточности в отдельной библиотеке или функции.

Выбор и реализация: После того, как вы завершили анализ и определили область, в которой вы бы хотели улучшить производительность, необходимо выбрать наиболее подходящую модель многопоточности. Если вы планируете сделать библиотеку просто безопасной для потоков, вы можете пропустить этап выбора модели. Если вы добавляете в вашу библиотеку многопоточность, у вас есть выбор из нескольких вариантов. Специалисты Intel настоятельно рекомендуют использовать средства абстракции потоков, которые позволяют разработчику определять параллельные задачи и управлять потоками с помощью исполняемой библиотеки. В первую очередь мы рекомендуем Intel® Threading Building Blocks и OpenMP*. Intel® Threading Building Blocks помогает реализовать многопоточность с помощью библиотеки шаблонов C++. OpenMP* помогает реализовать поддержку многопоточности с помощью директив pragma (OpenMP* поддерживается компилятором Intel и другими распространенными компиляторами). Последний и самый трудоемкий способ – это использование потоков операционной системы или POSIX*, что потребует больших затрат времени и сопряжено с наибольшим риском возникновения ошибок. В конце нашей статьи приводятся ссылки на материалы, содержащие более подробные сведения о реализации многопоточности, отладке и оптимизации.
Проверка правильности: Вне зависимости от того, решили ли вы сделать библиотеку многопоточной или безопасной для потоков, вам потребуется инструмент Intel® Thread Checker, который весьма полезен для проверки кода на распространенные ошибки многопоточности, в частности, на конкуренцию по доступу к данным. Если ваша библиотека планируется как безопасная для потоков, вы можете обойтись простой программой, которая будет одновременно передавать библиотеке вызовы из нескольких потоков. Если вы планируете добавить в библиотеку многопоточность, используйте Thread Checker вместе с программой, которая создает нагрузку для исполнения библиотеки. Инструмент Intel® Thread Checker выдаст вам номера строк исходного кода, где происходит конкуренция. Intel® Thread Checker может также использоваться для проверки библиотек, полученных от других групп разработчиков, на безопасность для потоков. Напишите программу, которая порождает множество вызовов к библиотеке из различных потоков, и проверьте результаты с помощью Intel® Thread Checker. Если исходный код недоступен, этот инструмент сообщит о дизассемблировании и развернет стек вызовов функции.
Оптимизация: Если вы добились стабильной работы приложения, необходимо позаботиться об оптимизации многопоточной библиотеки или программного кода, вызывающего безопасную для потоков библиотеку. Intel® Thread Profiler – это утилита для оценки производительности, которая позволяет разработчикам выявить факты дисбаланса рабочей нагрузки и избыточные вызовы синхронизации, которые отрицательно сказываются на производительности. Небольшой анализ часто дает возможность для существенного улучшения производительности.
Резюме
Многоядерные платформы получают все большее распространение, при этом быстро растет количество ядер в таких платформах. Программное обеспечение с поддержкой многопоточности для повышения производительности имеет очевидные преимущества по сравнению с аналогичным ПО без соответствующей оптимизации. Разработка или лицензирование программных библиотек, которые оптимизированы для многопоточной производительности или являются безопасными для потоков и могут входить в комплект различных продуктов, – это эффективный способ реализации возможностей многоядерной архитектуры современных систем.
AutoCAD 2007 – одно из приложений, которое написано с применением данного подхода и демонстрирует отличные результаты. Не вызывает сомнения, что разработчикам следует оптимизировать свои программы для многоядерной среды, и удобным решением этой задачи может стать написание соответствующих библиотек. Применяя рассмотренный нами подход, вы можете ускорить реализацию потоков в рамках цикла разработки вашего продукта и добиться повышения его производительности. Программные решения Intel помогают разработчикам выявлять области кода для распараллеливания, предлагают эффективные модели многопоточной абстракции и позволяют обнаруживать и устранять проблемы конкуренции за данные и «узкие места» в производительности.
Ознакомительные версии всех инструментов Intel доступны для загрузки на странице
http://www.intel.com/software/products.
Ссылки по теме
Kirkegaard, Knud J. et. al, ”Methodology, Tools and Techniques to Parallelize Large-Scale Applications: A Case Study”. Intel Technology Journal, Том 11, Выпуск 04, также доступно по ссылке (http://www.intel.com/technology/itj/2007/v11i4/6-tools/1-abstract.htm)
Threading Methodology: Principles and Practices, Version 2.0
