УДК 004.272 ПАРАЛЛЕЛЬНАЯ РЕАЛИЗАЦИЯ АЛГОРИТМОВ ПОИСКА СТРОК Магарёв В.В. Северо-Казахстанский государственный университет им. М.Козыбаева, Петропавловск Научный руководитель – Куликов В.П. Цель данной работы – путем распараллеливания существующих алгоритмов, добиться увеличения производительности поиска информации. Были отобраны 10 наиболее распространенных алгоритмов поиска строк, которые представлены в таблице. Вычисления произведены на видеокарте с использованием технологии NVIDIA CUDA, предназначенной для разработки приложений для массивнопараллельных вычислительных устройств. На сегодняшний момент поддерживаемыми устройствами являются все GPU компании NVIDIA, начиная с серии GeForce8, а также специализированные для решения расчетных задач GPU семейства Tesla. Основные преимущества CUDA: простота (все программы пишутся на «расширенном» языке С), наличие хорошей документации, набор готовых инструментов, включающих профайлер, набор готовых библиотек, кроссплатформенность (поддерживаются Microsoft Windows, Linux и Mac OS X). CUDA строится на концепции, что GPU (называемый устройством, device) выступает в роли массивно-параллельного сопроцессора к CPU (называемому host). Программа на CUDA задействует как CPU, так и GPU. При этом обычный (последовательный, то есть непараллельный) код выполняется на CPU, а для массивно-параллельных вычислений соответствующий код выполняется на GPU как набор одновременно выполняющихся нитей (потоков, threads). Таким образом, GPU рассматривается как специализированное вычислительное устройство, которое: • является сопроцессором к CPU; • обладает собственной памятью; • обладает возможностью параллельного выполнения огромного количества отдельных нитей. Важным моментом является то, что хотя подобный подход очень похож на работу с SIMD-моделью, есть и принципиальные отличия (компания NVIDIA использует термин SIMT – Single Instruction, Multiple Thread). Нити разбиваются на группы по 32 нити, называемые warps. Только нити в пределах одного warp выполняются физически одновременно. Нити из разных warp могут находиться на разных стадиях выполнения программы. Для решения задач CUDA использует очень большое количество параллельно выполняемых нитей, при этом обычно каждой нити соответствует один элемент вычисляемых данных. Все запущенные на выполнение нити организованы в следующую иерархию (рисунок). Верхний уровень иерархии – сетка (grid) – соответствует всем нитям, выполняющим данное ядро. Верхний уровень представляет из себя одномерный или двухмерный массив блоков (block). Каждый блок – это одномерный, двухмерный или трехмерный массив нитей (thread). При этом все блоки имеют одинаковую размерность и размер [1]. Поскольку модель вычислений, используемая в CUDA похожа на модель SIMD, т.е. параллелизм на уровне данных, то напрашивается следующий принцип параллельного выполнения алгоритмов: исходная строка разбивается на множество мелких подстрок, затем каждый вычислительный поток ищет образец в своей подстроке, используя последовательный алгоритм. Все алгоритмы тестированы на текстовом файле размером 100 Мбайт, в конце файла находится искомая строка, длиной 6 символов. Если алгоритм требует предварительных вычислений перед началом поиска, то все предвычисления выполняются на процессоре, даже в параллельном варианте алгоритма, поэтому время предвычислений будем игнорировать, т.к. оно будет одинаковым в обоих случаях. Также не будем учитывать время необходимое на загрузку файла в оперативную память (оно одинаково). Итак, замеряем время работы непосредственно алгоритмов поиска, Рисунок 1. Иерархия нитей в CUDA без «накладных расходов». Однако следует учесть, что специфика вычислений на видеокарте требует, чтобы обрабатываемые данные находились во внутренней памяти видеокарты, поэтому для получения достоверных результатов следует к основному времени выполнения алгоритма на видеокарте суммировать время загрузки и выгрузки данных. Для измерения времени работы последовательных версий алгоритмов использовался профилировщик Intel VTune, а для параллельных алгоритмов – NVIDIA Compute Visual Profiler. Чтобы оценить степень влияния оптимизирующего компилятора, все программы будем компилировать в режимах debug (неоптимизированный) и release (оптимизированный). Итоговые результаты приведены в таблице (время измерялось в миллисекундах). Таблица 1 Алгоритм Грубой силы Рабина-Карпа ДКА Морриса-Пратта Кнута-Морриса-Пратта Бойера-Мура Бойера-Мура-Хорспула Быстрый поиск Райта Обращения сегмента Последовательный Debug Release 834 461 1078 454 910 599 1343 525 1412 543 443 246 432 285 341 181 369 261 363 192 Параллельный Debug Release 295 291 464 478 283 278 318 288 309 291 286 164 138 137 162 167 137 146 140 141 Оптимизация дает значительный прирост производительности для последовательных алгоритмов, однако для параллельных она практически не имеет смысла. В среднем, прирост производительности для параллельных алгоритмов составляет 2.9 для неоптимизированного варианта, и 1.6 для оптимизированного. Из этого следует, что использование параллельных вычислений для поиска строк оправдывает себя. Литература 1. Боресков А.В., Харламов А.А. Основы работы с технологией CUDA. – М.: ДМК пресс, 2010, 232 c.