Библиотека сайта rus-linux.net
Обработка больших объемов данных в биоинформатике
Глава 12 из книги "Производительность приложений с открытым исходным кодом".
Оригинал: Working with Big Data in Bioinformatics
Авторы: Eric McDonald, C. Titus Brown
Перевод: А.Панин
Параллельное исполнение
С распространением многоядерных архитектур центральных процессоров в современном мире возникает желание воспользоваться их преимуществами. Однако, в отличие от работы во многих других областях исследований, таких, как вычислительная гидродинамика или молекулярная динамика, наша работа в связанной с обработкой больших объемов данных области исследований зависит от высокой пропускной способности механизма обработки данных - после определенного момента производительность выполняющихся в параллельном режиме задач будет в значительной степени ограничена производительностью операций ввода/вывода. После преодоления этого момента создание дополнительных потоков не будет оказывать положительного воздействия на производительность приложения, так как пропускная способность хранилища данных является максимальной и большое количество потоков просто приводит к повышению количества блокировок и времени ожидания завершения операций ввода/вывода. Исходя из сказанного, использование дополнительных потоков может оказать положительное воздействие на производительность приложения, особенно в том случае, если обрабатываемые данные хранятся в физической оперативной памяти, которая обычно имеет гораздо более высокую пропускную способность, чем сетевое хранилище. Как говорилось ранее, мы реализовали буфер для предварительного чтения данных, совместив его с механизмом прямого чтения данных. Множество потоков может использовать этот буфер; более подробно об этом мы поговорим ниже. Производительность операций ввода/вывода не является единственным ограниченным ресурсом, который должен разделяться между множеством потоков. Хэш-таблицы, используемые для подсчета k-меров являются еще одним таким ресурсом. Методика разделения доступа к ним также будет обсуждаться ниже.
Безопасность потоков и многопоточное приложение
Перед обсуждением подробностей реализации, может оказаться полезным прояснение некоторых терминов. Люди обычно путают термины "потокобезопасное" и "многопоточное". В том случае, если какой-либо примитив является потокобезопасным, он может быть доступен для множества потоков без опасений насчет порчи данных при их чтении или сохранении. В том случае, если примитив является многопоточным, он одновременно выполняет множество своих задач, используя для этого множество потоков исполнения.
В ходе нашей работы по добавлению возможности многопоточного исполнения мы повторно смоделировали реализацию основных механизмов языка C++ с целью достижения безопасности использования потоков без каких-либо предположений об определенной схеме использования потоков или библиотеке. Поэтому модуль threading
языка программирования Python может применяться в сценариях, которые используют обертку на языке Python над основной реализацией или программный компонент на языке C++, созданный как надстройка над основной реализацией, может использовать более высокоуровневую абстракцию, такую, как OpenMP, о которой мы говорили ранее, или же явно реализовывать функции работы с потоками, например, с помощью библиотеки pthreads. Достижение такой независимости от схемы использования потоков и предоставление гарантий безопасности использования потоков без нарушения существующих интерфейсов библиотеки языка программирования C++ было интересной задачей для разработчиков программного обеспечения. Мы решили ее, предоставляя часть функций API в качестве потокобезопасных с возможностью поддержки их собственных специфичных для потоков объектов состояния. Эти объекты состояния находились в контейнере map
, реализованном в рамках стандартной библиотеки шаблонов языка программирования C++ (С++ Standard Template Library - STL), а идентифицирующие потоки числа в данном случае являлись ключами. Идентификационный номер для определенного потока выяснялся путем выполнения системного вызова для обращения к ядру операционной системы из самого этого потока. Такое решение приводило к выполнению небольшого объема дополнительной работы направленной на выяснение потоком своего идентификационного номера при каждом вызове функции, предоставляемой в рамках API, но оно позволяет изящно избежать проблемы нарушения существующих интерфейсов, которые были разработаны с учетом использования одного потока исполнения.
Операции поставщика данных и системы разбора
Многпроцессорные машины, которые часто можно встретить в мире вычислительных систем высокой производительности могут иметь по нескольку контроллеров памяти, причем один контроллер обычно располагается ближе (в плане дистанции передачи сигналов) к одному центральному процессору, чем к другому. Это архитектуры с неравномерным доступом к памяти (Non-Uniform Memory Access - NUMA). Особенностью работы с машинами такой архитектуры является то, что время получения данных из памяти может отличаться и в очень значительной степени зависеть от физического адреса. Так как программному обеспечению для исследований в области биоинформатики обычно требуется большой объем оперативной памяти для работы, оно обычно используется на машинах такого типа. Следовательно, в случае использования множества потоков, которые могут быть привязаны к различным узлам NUMA (NUMA nodes), локализация данных в физической оперативной памяти должна приниматься к рассмотрению. С этой целью мы разделяем наш буфер для предварительного чтения данных на множество сегментов в соответствии с количеством потоков исполнения. Каждый поток исполнения ответственен за резервирование своего определенного сегмента в буфере. Сегмент буфера управляется посредством объекта состояния, поддерживаемого для каждого из потоков.
Продолжение статьи: Операции со структурой Bloom Filter.