Quantcast
Channel: 平板电脑
Viewing all articles
Browse latest Browse all 500

GPU-Quicksort в OpenCL 2.0: вложенные параллельные вычисления и сканирование групп обработки

$
0
0

Введение

В этом учебном руководстве показано использование двух полезных компонентов OpenCL™ 2.0: функций enqueue_kernel, позволяющих помещать в очередь ядра устройства, а также work_group_scan_exclusive_add и work_group_scan_inclusive_add, двух новых наборов функций групп обработки, которые были добавлены в OpenCL 2.0 для упрощения сканирования и упрощения операций над рабочими элементами в составе групп. В этом учебном руководстве эти функции демонстрируются в нашей собственной реализации алгоритма GPU-Quicksort в OpenCL, который, насколько мы знаем, является первой известной реализацией этого алгоритма в OpenCL. В учебном руководстве показан важный подход к проектированию: постановка в очередь ядер NDRange размера 1 для операций обслуживания и планирования, ранее выполнявшихся только на ЦП.

В этом руководстве вы узнаете об использовании инструментов Intel® для OpenCL. Пакет Intel® SDK для приложений OpenCL™ — это платформа разработки приложений OpenCL™ на архитектуре Intel®. Помимо SDK, инструмент Intel® VTune™ Amplifier XE применяется для анализа и оптимизации приложений на платформах Intel как на ЦП, так и на ГП, и этот инструмент поддерживает OpenCL. Дополнительные сведения об инструментах Intel для OpenCL см. по адресу http://intel.com/software/opencl.

В создании, редактировании и публикации этой статьи, в разработке кода и монтаже видео мне помогли Дипти Джоши (Deepti Joshi), Аллен Хакс (Allen Hux), Аарон Кунце (Aaron Kunze), Диллон Шарлет (Dillon Sharlet), Адам Лейк (Adam Lake), Михаль Мроцек (Michal Mrozek), Бен Эшбоу (Ben Ashbaugh), Аял Закс (Ayal Zaks), Юрий Кулаков (Yuri Kulakov), Джозеф Уэллс (Joseph Wells), Линн Пэтнэм (Lynn Putnam), Джерри Боу (Jerry Baugh), Джерри Макэр (Jerry Makare) и Крис Дэйвис (Chris Davis). Спасибо им! Кроме того, сердечно благодарю мою жену Эллен и моих детей Майкла и Селин за их непоколебимую поддержку и понимание.


Краткий курс истории алгоритма быстрой сортировки

Изобретатель алгоритма быстрой сортировки — сэр Чарльз Энтони Ричард («Тони») Хоар (C.A.R. Hoare). Он изобрел этот алгоритм в 1960 году во время обучения в аспирантуре Московского государственного университета, где он изучал теорию вероятности под руководством профессора Андрея Николаевича Колмогорова. Тони Хоар выучил русский язык во время службы в Королевском флоте, где его дядя был командиром корабля. Работая над проблематикой машинного перевода с одного языка на другой, Хоар пытался придумать алгоритм сортировки по возрастанию для слов каждого входящего предложения на русском языке. Основной принцип алгоритма быстрой сортировки заключается в следующем: сортируемая последовательность разделяется вокруг так называемого опорного элемента. Опорный элемент может быть выбран из последовательности различными способами, при этом все элементы меньше опорного размещаются в левой части массива, все элементы больше опорного размещаются справа, а элементы, равные опорному, размещаются в середине массива. После разделения массива алгоритм быстрой сортировки снова применяется к левой и правой частям последовательности.Partitioning Sequence Quicksort

Рисунок 1. Разделение последовательности в алгоритме быстрой сортировки


Краткое введение в алгоритм GPU-Quicksort

Первый известный алгоритм быстрой сортировки для ЦП разработали и описали Шубхабрата Сенгупта (Shubhabrata Sengupta), Марк Харрис (Mark Harris), Яо Чжань (Yao Zhang) и Джон Оуэнс (John D. Owens) в своей работе «Примитивы сканирования для вычислений на ГП». Представленный здесь алгоритм GPU-Quicksort впервые описали Дэниэл Сидермэн (Daniel Cederman) и Филиппас Цигас (Philippas Tsigas) в работе «GPU-Quicksort: практическая реализация алгоритма быстрой сортировки для графических процессоров»; он представляет собой усовершенствованную версию первого алгоритма быстрой сортировки для ГП. Описываемый алгоритм был изначально написан на CUDA* для запуска на дискретных видеоадаптерах Nvidia*, но его очень легко реализовать и в OpenCL для запуска на любом оборудовании, поддерживающем API OpenCL. Этот алгоритм разработан таким образом, чтобы использовать высокую пропускную способность ГП, он хорошо работает с драйверами OpenCL 1.2 (Intel® HD Graphics 4600) и OpenCL 2.0 (Intel® HD Graphics 4600, Intel® HD Graphics 5300 и более поздние версии). Отличный обзор алгоритма GPU-Quicksort приводится в разделе 3.1 статьи Сидермэна и Цигаса.

Как и первоначальный алгоритм быстрой сортировки, GPU-Quicksort рекурсивно разделяет последовательность элементов вокруг опорного элемента вплоть до сортировки всей последовательности. Поскольку этот алгоритм написан для ГП, он включает два этапа, где на первой этапе несколько групп обработки обрабатывают разные части одной и той же последовательности до тех пор, пока части последовательности не становятся достаточно маленькими, чтобы их можно было полностью отсортировать каждой рабочей группой на втором этапе.

Основной принцип алгоритма GPU-Quicksort состоит в разделении входной последовательности на блоки и назначении их разным рабочим группам. Каждая рабочая группа участвует в разделении входной последовательности по опорным элементам в двухэтапном процессе: сначала каждый элемент группы обработки вычисляет количество элементов, которые меньше и больше опорного элемента в своем блоке. Встроенные функции сложения со сканированием используются для вычисления суммы нарастающим итогом элементов, которые меньше и больше опорного. Дополнительный массив служит для выделения пространства для каждой группы обработки. На втором проходе элементы меньше и больше опорного записываются в выделенное пространство. И наконец, последняя группа обработки заполняет промежуток значением опорного элемента.

Partitioning Sequence GPU Quicksort

Рисунок 2. Разделение последовательности в алгоритме GPU-Quicksort


Алгоритм GPU-Quicksort в OpenCL 1.2

GPU-Quicksort в OpenCL 1.2 — это непосредственная реализация алгоритма, описанного в статье Сидермэна и Цигаса. Три части представляют собой фрагмент ЦП, реализуемый функцией GPUQSort; ядро первого этапа ГП реализовано функцией ядра OpenCL gqsort_kernel OpenCL, а ядро второго этапа ГП — функцией ядра OpenCL lqsort_kernel. Функция GPUQSort последовательно запускает gqsort_kernel до разделения первоначальной последовательности на настолько маленькие блоки, что каждый блок может быть отсортирован одной группой обработки. Затем GPUQSort запускает lqsort_kernel для завершения работы сортировки.

Для реализации GPU-Quicksort требуются барьеры и атомарные функции, доступные в OpenCL 1.2 и поддерживающиеся практически всем современным оборудованием, поддерживающим OpenCL 1.2. В частности, в реализации gqsort_kernel используются функции atomic_add, atomic_sub и atomic_dec. Барьеры широко применяются как в gqsort_kernel, так и в lqsort_kernel.

Использование OpenCL 1.2 связано со следующими недостатками:

  1. нехватка примитивов сканирования групп обработки, из-за чего для реализации сумм префиксов требовалось использовать алгоритм, описанный в известной статье Гая Блеллока (Guy Blelloch);
  2. частый обмен данными между ЦП и ГП для запуска gqsort и последующего запуска lqsort;
  3. необходимость выполнения операций обслуживания перед запуском ядер в ЦП, хотя необходимые данные на этот момент уже готовы и доступны в ГП.

Все эти три недостатка удалось устранить в OpenCL 2.0. Вы увидите, что путем устранения этих недостатков удалось значительно повысить производительность алгоритма.


Преобразование GPU-Quicksort для OpenCL 2.0

В этом разделе описываются изменения, сделанные при переходе от OpenCL 1.2 к OpenCL 2.0, где используются новые возможности постановки в очередь на стороне устройства и сканирования групп обработки.

Функции групп обработки могут повысить производительность и читаемость кода OpenCL C

Самый первый и простейший этап преобразования GPU-Quicksort для OpenCL 2.0 — использование готовых функций сканирования групп обработки: work_group_scan_inclusive_add и work_group_scan_exclusive_add в gqsort_kernel и lqsort_kernel. Мы добились не только заметного прироста производительности (примерно на 8 %), но и повышения четкости и ясности кода, простоты его обслуживания. Размер кода, связанного с вычислением сумм префиксов, удалось снизить почти втрое.

Блоки дают возможность воспользоваться вложенной параллельной обработкой с помощью постановки в очередь на стороне устройства

Затем мы переделали логику, сортирующую записи после каждого запуска gqsort_kernel в записи, пригодные для обработки функцией lqsort_kernel, и в записи, требующие дальнейшего деления, а также вычисляющую блоки и родительские записи и запускающую либо gqsort_kernel, либо lqsort_kernel из C++ в OpenCL C. Функция lqsort_kernel будет запускаться только в самом конце запуска GPU-Quicksort. В нашей первой реализации эта логика находилась в gqsort_kernel, вследствие чего требовались дополнительные глобальные переменные и атомарные функции, а функция gqsort_kernel работала относительно медленно, хотя общая производительность несколько возросла по сравнению с версией для OpenCL 1.2. Мы переместили эту логику в отдельную функцию relauncher_kernel, которая теперь запускается для самого первого ядра и по окончании каждого запуска gqsort_kernel.

       // now let’s recompute and relaunch
       if (get_global_id(0) == 0) {
           uint num_workgroups = get_num_groups(0);
           queue_t q = get_default_queue();
           enqueue_kernel(q, CLK_ENQUEUE_FLAGS_WAIT_KERNEL,
                          ndrange_1D(1),
                          ^{ relauncher_kernel(d, dn,
                             blocks, parents, result, work, done, done_size,
                             MAXSEQ, num_workgroups);
                           });
       }

Обратите внимание, что relauncher_kernel запускается только для одиночных рабочих элементов (ndrange = 1). Изоляция логики relauncher от qsort_kernel позволила значительно упростить qsort_kernel и повысить производительность алгоритма.

Несколько замечаний о постановке relauncher_kernel в очередь: мы используем новую возможность OpenCL 2.0 — блоки. Поскольку ядрам не требуются локальные аргументы памяти, мы используем блоки типа void (^) (void). Блоки аналогичны лямбда-функциям C++, но у них другой синтаксис. Блок получает переданные ему параметры, поэтому упраздняется трудоемкая работа по заданию аргументов ядра с помощью эквивалента clSetKernelArg. Также следует отметить, что num_workgroups вычисляется вне блока, а вычисленное значение передается в блок. Обратите внимание, что в приведенном выше примере кода num_workgroups записывается в значение: это именно то, что нам нужно. Если бы мы использовали непосредственно get_num_groups(0) вместо num_workgroups, то эту функцию следовало бы вызывать после постановки в очередь и выполнения блока, содержащего вызов relauncher_kernel, поэтому значение, переданное в relauncher_kernel, было бы равно 1, а не количеству групп обработки в gqsort_kernel. Дополнительные сведения о синтаксисе блоков в OpenCL C см. в спецификации OpenCL C [3].

Функция relauncher_kernel запускает либо gqsort_kernel, либо lqsort_kernel в зависимости от того, осталась ли доступна работа (work_size != 0) для gqsort_kernel.

       if (work_size != 0) {
           // Calculate block size, parents and blocks
           ...

           enqueue_kernel(q, CLK_ENQUEUE_FLAGS_WAIT_KERNEL,
                          ndrange_1D(GQSORT_LOCAL_WORKGROUP_SIZE * blocks_size,
                                     GQSORT_LOCAL_WORKGROUP_SIZE),
                          ^{ gqsort_kernel(d, dn,
                                           blocks, parents, result, work, done,
                                           done_size, MAXSEQ, 0); });
       } else {
           enqueue_kernel(q, CLK_ENQUEUE_FLAGS_WAIT_KERNEL,
                     ndrange_1D(LQSORT_LOCAL_WORKGROUP_SIZE * done_size,
                                LQSORT_LOCAL_WORKGROUP_SIZE),
                     ^{ lqsort_kernel(d, dn, done); });
       }

После запуска первого relauncher_kernel из ЦП все последующие запуски ядер выполняются из ГП. После полной сортировки входных данных управление возвращается центральному процессору.

GPU-Quicksort kernel enqueueing sequence in OpenCL 2.0

Рисунок 3.Последовательность кодирования ядра GPU-Quicksort в OpenCL 2.0


Требования для учебной программы

Для сборки и запуска этой учебной программы нужен ПК, соответствующий следующим требованиям.

  • Процессор серии Intel® Core™ с кодовым названием Broadwell.
  • Microsoft Windows* 8 или 8.1.
  • Intel® SDK для приложений OpenCL™ версии 2014 R2 или более поздней.
  • Microsoft Visual Studio* 2012 или более поздней версии.

Запуск учебной программы

Учебная программа представляет собой консольное приложение, формирующее массив случайных целых чисел без знака размером Ширина*Высота. Затем выполняется сортировка копий этого массива с помощью std::sort, обычной однопоточной быстрой сортировки, за которой следует ряд итераций сортировки с помощью GPU-Quicksort в OpenCL 2.0.  Эта учебная программа поддерживает несколько параметров командной строки.

ПараметрОписание
-h, -?Отображение текста справки и выход.
[num test iterations]Количество прохождений алгоритма GPU-Quicksort для одних и тех же данных.
[cpu|gpu]Запуск учебной программы с помощью ЦП или ГП OpenCL.
[intel|amd|nvidia]Поставщик устройства OpenCL, на котором запускается программа
[Width]«Ширина» ввода — упрощает ввод больших чисел.
[Height]«Высота» ввода — упрощает ввод больших чисел (например, можно указать 8192 8192 вместо 67 млн элементов).
[show_CL|no_show_CL]Отображение или отсутствие подробной информации об устройстве OpenCL.

Попробуйте запустить учебную программу с параметрами 5 gpu intel 8192 8192 show_CL.


Заключение

Процессор Intel с ГП Intel® HD Graphics 5300 или более мощным представляет собой сложное оборудование, которое в сочетании с драйвером, поддерживающим OpenCL 2.0, способно, однако, значительно повысить производительность работы кода OpenCL. В OpenCL 2.0 поддерживается ряд удобных и мощных возможностей для программирования ГП. Мы рассмотрели только две таких возможности: функцию enqueue_kernel и функции work_group_scan_exclusive_add и work_group_scan_inclusive_add. Эти функции позволяют добиться резкого повышения производительности, они упрощают код и делают его более удобочитаемым. Прочтите другие учебные материалы Intel о других возможностях OpenCL на веб-сайте поддержки Intel® SDK для приложений OpenCL™. 

Справочные материалы

  1. Intel® SDK для приложений OpenCL™ — руководство по оптимизации.
  2. Спецификация API Khronos OpenCL 2.0.
  3. Спецификация Khronos OpenCL 2.0 для языка C.
  4. Статья о быстрой сортировкев Википедии.
  5. Начало моей работы в компании «Эллиот», Тони Хоар.
  6. Примитивы сканирования для вычислений на ГПШубхабрата Сенгпута, Марк Харрис, Яо Чжань и Джон Оуэнс,«Графическое оборудование», 2007 г., стр. 97–106, август 2007 г.
  7. GPU-Quicksort: практическая реализация алгоритма быстрой сортировки для графических процессоровДэниэл Сидермэн и Филиппас Цигас, «Журнал экспериментальных алгоритмов», том 14, 2009 г., статья № 4.
  8. Суммы префиксов и их применение, Гай Блеллок
  9. Документация в Интернете по функциям enqueue_kernel
  10. Документация в Интернете по функциям сканирования групп обработки с включением.
  11. Документация в Интернете по функциям сканирования групп обработки с исключением. /li>
  12. Intel® SDK для приложений OpenCL™.
  13. Intel® SDK 2013 R2 для приложений OpenCL™ — руководство по оптимизации.
  14. Полный контрольный список для оптимизированного приложения OpenCL.
  15. Написание оптимального кода OpenCL с помощью Intel® OpenCL SDK  - в формате PDF.

Об авторе

Роберт Иоффе (Robert Ioffe) работает в должности технического инженера-консультанта в отделе Software and Solutions Group корпорации Intel. Роберт — опытный специалист по программированию OpenCL и оптимизации нагрузки OpenCL на ГП Intel Iris и Intel Iris Pro, он прекрасно знает графическое оборудование Intel. Роберт принимал активное участие в разработке стандартов Khronos, занимался прототипированием самых последних функций и обеспечением их работоспособности в архитектуре Intel. В последнее время Роберт работал над прототипированием возможностей вложенной параллельной обработки (функции enqueue_kernel) OpenCL 2.0; он написал ряд примеров, демонстрирующих эту функциональность, включая алгоритм GPU-Quicksort для OpenCL 2.0. Кроме того, он записал и выпустил два видеоролика по оптимизации ядер OpenCL; сейчас он работает над третьим видеороликом, посвященным возможностям вложенной параллельной обработки.

Также вас могут заинтересовать следующие материалы.

Ковер Серпинского в OpenCL 2.0

Оптимизация простых ядер OpenCL: оптимизация модульного ядра

Оптимизация простых ядер OpenCL: оптимизация ядра Собеля

Загрузить код

  • tutorial
  • openCL
  • OpenCL 2.0
  • GPU-Quicksort
  • Nested parallelism
  • device-side enqueue
  • enqueue_kernel
  • work_group_scan_exclusive
  • work_group_scan_inclusive
  • 开发人员
  • 英特尔 AppUp® 开发人员
  • 合作伙伴
  • 教授
  • 学生
  • Android*
  • Apple OS X*
  • Linux*
  • Microsoft Windows* 8.x
  • 安卓*
  • 游戏开发
  • Windows*
  • C/C++
  • 高级
  • 中级
  • 英特尔® Integrated Native Developer Experience
  • OpenCL™ Code Builder
  • 面向 OpenCL™ 应用的英特尔® 软件开发套件
  • 英特尔® Integrated Native Developer Experience Build Edition(适用 OS X*)
  • OpenCL*
  • 游戏开发
  • 图形
  • 英特尔® 酷睿™ 处理器
  • 优化
  • 并行计算
  • 嵌入式
  • 笔记本电脑
  • 电话
  • 服务器
  • 平板电脑
  • 桌面
  • URL
  • 代码样本
  • 提升性能
  • Windows*
  • 上次更新时间: 

    星期三, 2015, 三月 4

    Viewing all articles
    Browse latest Browse all 500

    Trending Articles



    <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>