Как ускорить вычисления и повысить производительность программ с помощью принципов массивного параллелизма и OpenCL
Стандартный подход к написанию программ является линейным – операция b выполняется после завершения операции a. Но что делать в случае если таких операций десятки тысяч, а задача требует быстрого произведения данных операций?
Допустим, вы разрабатываете приложение с искусственным интеллектом для распознавания предметов основываясь на данных получаемых с сенсора, или пишете графическое приложение, которое требует множество вычислений на каждом из пикселей. Как сделать так, чтобы обработка информации происходила в реальном времени, а задержка между получением информации, её обработкой и выдачей результата была минимальной?
Те кто сталкивался с данной проблемой, наверняка знают про многопоточность (multithreading) — способ параллельного запуска определённых фрагментов кода для ускорения обработки этого кода. Но, к сожалению, этот метод подходит не всегда, и когда требуется произведение одинаковых операций на большом количестве данных, гораздо эффективнее будет прибегнуть к массивному параллелизму и использовать фреймворки OpenCL или CUDA.
Как скачать opencl.dll и исправить ошибки при запуске игр и программ
В этой статье мы познакомимся с массивным параллелизмом и напишем программу для параллельных вычислений, используя фреймворк OpenCL.
OpenCL (Open Computing Language) — фреймворк разработанный Apple в 2008 году и поддерживаемый Khronos Group с 2009 года. Он позволяет создавать программы для параллельного исполнения на различных вычислительных девайсах (CPU и GPU), упакованные в «кернели»-ядра (kernels) — части кода, которые будут отправлены на вычислительный девайс для произведения каких-то операций.
OpenCL — замечательный инструмент, который может ускорить вашу программу в десятки, если не сотнитысячи раз. К сожалению, из-за отсутствия простых и доступных гайдов по данной спецификации, в особенности на русском языке, а так-же особенностям работы с ней (о чём мы поговорим чуть позже), начинающему разработчику может быть очень трудно разобраться в теме и попытки изучить спецификацию остаются безуспешными. Это большое упущение, и цель этой статьи — не только доступно и подробно рассказать о спецификации и продемонстрировать как работать с ней, но и написать гайд, который мне хотелось бы прочитать самому когда я начал изучать OpenCL.
Давайте параллелить
Массивный параллелизм — топик для целого цикла лекций по информатике и инженерии, по этому давайте ограничимся небольшим обобщением, чтобы понимать с чем мы имеем дело.
В линейном программировании мы имеем чёткую последовательность действий: a, b, c, d; действие b не будет выполнено до того как завершиться a и c не будет выполнено пока не завершиться b. Но что делать, если нам, например, требуется найти суммы элементов из двух массивов (листов), и в каждом массиве по 100,000 элементов? Последовательное вычисление заняло бы достаточно долгое время, так-как нам пришлось бы совершить минимум 100,000 операций. А что если такая процедура требует многочисленного повторения и результат нужен в реальном времени с минимальной задержкой? Тут нам и приходит на помощь массивный параллелизм!
Intel SDK for OpenCL Applications 2012
Допустим, мы хотим вычислить суммы 0 + 3, 1 + 2, 2 + 1, 3 + 0 и записать результаты в массив. В линейном программировании, мы воспользуемся циклом for или while, где операции будут выполняться последовательно, и схема вычислений будет выглядеть примерно так:
Через последовательную итерацию, мы будем выполнять первое действие, записывать результат в массив и переходить к следующему действию. Поскольку для выполнения данных вычислений нам требуется 4 итерации, для наглядности, давайте обозначим что время (t) потраченное на выполнение данных операций равняется 4.
Массивный параллелизм позволяет нам сформировать и отправить данную задачу (0 + 3 и т.д.) для выполнения используя ресурсы, например, видеокарты — она имеет десятки, сотни, тысячи вычислительных единиц (ядер), которые могут производить операции параллельно, независимо друг от друга.
Имея достаточно место в массиве, нам не обязательно ждать пока заполнится предыдущий элемент массива перед тем как записывать следующий, мы можем присвоить задачам индексы, соответствующие их «месту» в массиве, и тем самым, записывать ответ каждого вычисления в правильное место, не дожидаясь пока предыдущая задача будет выполнена.
Другими словами, если 0 + 3 — вычисление номер один, а 1 + 2 — вычисление номер два, мы можем посчитать 1 + 2 и записать ответ во второе место в массиве (res[1]) не зависимо от того записан ли ответ 0 + 3 в первое место (res[0]). При этом, если мы попытаемся одновременно записать информацию в ячейки res[0] (первое место) и res[1] (второе место), у нас не возникнет ошибки.
Таким образом, с помощью массивного параллелизма мы можем одновременно и независимо друг от друга выполнить все нужные нам операции, и записать все ответы в массив, содержащий результаты, всего за одно действие, тем самым, сократив t до 1. Мы только что сократили временную сложность алгоритма (время работы алгоритма) до константного значения 1 (одно действие).
Две системы
Перед тем как мы перейдём к практике, важно прояснить специфику работы с OpenCL и разобраться как переписывать линейный код в параллельном формате. Не волнуйтесь — это совсем не сложно!
Кернель (kernel, вектор — функция, отправляемая на вычислительный девайс в контексте работы OpenCL) и хост (host — код, вызывающий OpenCL; ваш код) существуют, по большему счёту, изолированно друг от друга.
Компиляция и запуск кернеля OpenCL происходят внутри вашего кода, во время его исполнения (онлайн, или runtime execution). Так-же, важно понимать что кернель и хост не имеют общего буфера памяти и мы не можем динамически выделять память (с помощью malloc() и подобных) внутри кернеля. Обмен информацией между двумя системами происходит посредством отправления между ними заранее выделенных регионов памяти.
Другими словами, если у меня в хосте есть объект Х, для того чтобы обратится к нему из кернеля OpenCL, мне для начала нужно его туда отправить. Если вы всё ещё не до конца поняли о чём идёт речь — читайте дальше и всё прояснится!
Модель памяти OpenCL выглядит так:
При создании контекста OpenCL (среда в которой существует данный инстанс OpenCL), мы обозначаем нашу задачу как NDRange — общий размер вычислительной сетки — количество вычислений которые будут выполнены (в примере с суммами использованном выше, NDRange = 4). Информацию записанную в глобальную память (global memory) мы можем получить из любого элемента NDRange.
При отправке задачи на девайс OpenCL (например, видеокарту компьютера), наш NDRange разбивается на рабочие группы (work-groups) — локальная память (local memory), которые содержат в себе рабочие единицы (work-items, изолированные инстансы кернеля) — приватная память (private memory).
Мы не можем считать объект из локальной памяти в глобальную. Это значит что значение существующее внутри одной рабочей группы не будет доступно из другой группы.
Локальная память имеет доступ к объектам из глобальной памяти, но не может получить информацию из приватной памяти. Наконец, приватная память может считывать из глобальной и локальной памяти, но всё что существует внутри её, остаётся в ней.
Стоит добавить, что от выбора памяти зависит скорость выполнения программы. Так, запрос к глобальной памяти является самым времязатратным, и работать с глобальной памятью стоит только в случае необходимости.
В большинстве случаев, будет грамотнее объявить переменную в локальной или приватной памяти (нет смысла создавать переменную в глобальной памяти, если она не используется за пределами одной рабочей единицы). Так-же, зачастую будет грамотным решением перевести глобальный объект в локальную или приватную память, после получения его внутри кернеля, если требуется произвести с ним множество операций внутри одной рабочей единицы, так-как это может значительно ускорить выполнение программы.
Для обмена данными между хостом и кернелем, внутри хоста мы создаём специальный буфер, содержащий информацию, нужную для вычислений на девайсе OpenCL, а так-же буфер с выделенным местом (памятью), в который будут записаны результаты вычислений кернеля. Эти элементы отправляются в кернель, он производит свои вычисления, записывает результат в буфер который мы специально для этого подготовили и отправляет его обратно в хост.
В общих чертах, схема работы с OpenCL выглядит следующим образом:
Источник: vc.ru
OpenCL. Что это такое и зачем он нужен? (если есть CUDA)
Многие, наверное, слышали или читали на хабре об OpenCL – новом стандарте для разработки приложений для гетерогенных систем. Именно так, это не стандарт для разработки приложений для GPU, как многие считают, OpenCL изначально задумывался как нечто большее: единый стандарт для написания приложений, которые должны исполняться в системе, где установлены различные по архитектуре процессоры, ускорители и платы расширения.
Предпосылки появления OpenCL
Основным местом, где можно встретить гетерогенные системы, являются высокопроизводительные вычисления: от моделирования физических процессов в пограничном слое до кодирования видео и рендеринга трехмерных сцен. Раньше подобные задачи решали применяя суперкомпьютеры либо очень мощные настольные системы. С появлением технологий NVidia CUDA/AMD Stream стало возможным относительно просто писать программы, использующие вычислительные возможности GPU.
Стоит отметить, что подобные программы создавались и раньше, но именно NVidiaа CUDA обеспечила рост популярности GPGPU за счет облегчения процесса создания GPGPU приложений. Первые GPGPU приложения в качестве ядер (kernel в CUDA и OpenCL) использовали шейдеры, а данные запаковывались в текстуры. Таким образом необходимо было быть хорошо знакомым OpenGL или DirectX. Чуть позже появился язык Brook, который немного упрощал жизнь программиста (на основе этого языка создавалась AMD Stream (в ней используется Brook+) ).
CUDA стала набирать обороты, а между тем (а точнее несколько ранее) в кузнице, расположенной глубоко под землей, у подножия горы Фуджи (Fuji), японскими инженерами был выкован процессор всевластия Cell (родился он в сотрудничестве IBM, Sony и Toshiba). В настоящее время Cell используется во всех суперкомпьютерах, поставляемых IBM, на его основе постоены самые производительные в мире суперкомпьютеры (по данным top500). Чуть менее года назад компания Toshiba объявила о выпуске платы расширения SpursEngine для PC для ускорения декодирования видео и прочих ресурсоемких операций, используя вычислительные блоки (SPE), разработанные для Cell. В википедии есть статья, в кратце описывающая SpursEngine и его отличия от Cell.
Примерно в то же время (около года назад) оживилась и S3 Graphics (на самом деле VIA), представив на суд общественности свой новый графический адаптер S3 Graphics Chrome 500. По заявлениям самой компании этот адаптер так же умеет ускорять всяческие вычисления. В комплекте с ним поставляется программный продукт (графический редактор), который использует все прелести такого ускорения. Описание технологии на сайте производителя.
Итак, что мы имеем: машина, на которой проводятся вычисления может содержать процессоры x86, x86-64, Itanium, SpursEngine (Cell), NVidia GPU, AMD GPU, VIA (S3 Graphics) GPU. Для каждого из этих типов процессов существует свой SDK (ну кроме разве что VIA), свой язык программирования и программная модель. То есть если Вы захотите чтобы ваш движок рендеринга или программа расчета нагрузок на крыло боинга 787 работала на простой рабочей станции, суперкомпьютере BlueGene, или компьютере оборудованном двумя ускорителями NVidia Tesla – Вам будет необходимо переписывать достаточно большую часть программы, так как каждая из платформ в силу своей архитектуры имеет набор жестких ограничений.
Так как программисты – народ ленивый, и не хотят писать одно и то же для 5 различных платформ с учетом всех особенностей и учиться использовать разные программные средства и модели, а заказчики – народ жадный и не хотят платить за программу для каждой платформы как за отдельный продукт и оплачивать курсы обучения для программистов, было решено создать некий единый стандарт для программ, исполняющихся в гетерогенной среде. Это означает, что программа, вообще говоря, должна быть способна исполняться на компьютере, в котором установлены одновременно GPU NVidia и AMD, Toshiba SpursEngine итд.
Решение проблемы
Для разработки открытого стандарта решили привлечь людей, у которых уже есть опыт (весьма успешный) в разработке подобного стандарта: Khronos Group, на чьей совести уже OpenGL и OpenML и еще много всего. OpenCL является торговой маркой Apple Inc., как сказано на сайте Khronos Group: «OpenCL is a trademark of Apple Inc., and is used under license by Khronos. The OpenCL logo and guidelines for its usage in association with Conformant products can be found here:
http://developer.apple.com/softwarelicensing/agreements/opencl.html». В разработке (и финансировании, конечно же), кроме Apple, участвовали такие воротилы IT как AMD, IBM, Activision Blizzard, Intel, NVidia итд. (полный список тут).
Компания NVidia особо не афишировала свое участие в проекте, и быстрыми темпами наращивала функциональность и производительность CUDA. Тем временем несколько ведущих инженеров NVidia участвовали в создании OpenCL. Вероятно, именно участие NVidia в большой мере определило синтаксическую и идеологическую схожесть OpenCL и CUDA. Впрочем программисты от этого только выиграли – проще будет перейти от CUDA к OpenCL при необходимости.
Первая версия стандарта была опубликована в конце 2008 года и с тех пор уже успела претерпеть несколько ревизий.
Почти сразу после того как стандарт был опубликован, компания NVidia заявила что поддержка OpenCL не составит никакой сложности для нее и в скором времени будет реализована в рамках GPU Computing SDK поверх CUDA Driver API. Ничего подобного от главного конкурента NVidia – AMD слышно не было.
Драйвер для OpenCL был выпущен NVidia и прошел проверку на совместимость со стандартом, но все еще доступен только для ограниченного круга людей – зарегистрированных разработчиков (заявку на регистрацию подать может любой желающий, в моем случае рассмотрение заняло 2 недели, после чего по почте пришло приглашение). Ограничения доступа к SDK и драйверам заставляют задуматься о том, что на данный момент существуют какие-то проблемы или ошибки, которые пока не удается исправить, то есть продукт все еще находится в стадии бета-тестирования.
Реализация OpenCL для NVidia была достаточно легкой задачей, так как основные идеи сходны: и CUDA и OpenCL – некоторые расширения языка С, со сходным синтаксисом, использующие одинаковую программную модель в качестве основной: Data Parallel (SIMD), так же OpenCL поддерживает Task Parallel programming model – модель, когда одновременно могут выполняться различные kernel (work-group содержит один элемент). О схожести двух технологий говорит даже то что NVidia выпустила специальный документ о том как писать для CUDA так, чтобы потом легко перейти на OpenCL.
Как обстоят дела на настоящий момент
Основной проблемой реализации OpenCL от NVidia является низкая производительность по сравнению с CUDA, но с каждым новым релизом драйверов производительность OpenCL под управлением CUDA все ближе подбирается к производительности CUDA приложений. По заявлениям разработчиков такой же путь проделала и производительность самих CUDA приложений – от сравнительно невысокой на ранний версиях драйверов до впечатляющей в настоящее время.
А что же делала в этот момент AMD? Ведь именно AMD (как сторонник открытых стандартов – закрытый PhysX vs. открытый Havoc; дорогой Intel Thread Profiler vs. бесплатный AMD CodeAnalyst) делала большие ставки на новую технологию, учитывая что AMD Stream не удавалось хоть сколь-нибудь соревноваться в популярности с NVidia CUDA – виною тому отставание Stream от CUDA в техническом плане.
Летом 2009 года компания AMD сделала заявление о поддержке и соответствии стандарту OpenCL в новой версии Stream SDK. На деле же оказалось, что поддержка была реализована только для CPU. Да, именно так, это ничему не противоречит – OpenCL стандарт для гетерогенных систем и ничего не мешает Вам запустить kernel на CPU, более того – это очень удобно в случае если в системе нет другого OpenCL устройства.
В таком случае программа будет продолжать работать, только медленнее. Или же вы можете задействовать все вычислительные мощности, которые есть в компьютере – как GPU так и CPU, хотя на практике это не имеет особого смысла, так как время исполнения kernel’ов которые исполняются на CPU будет намного больше тех что исполняются на GPU – скорость процессора станет узким местом. Зато для отладки приложений это более чем удобно.
Поддержка OpenCL для графических адаптеров AMD так же не заставила себя долго ждать – по последним сообщениям компании версия для графических чипов сейчас находится на стадии подтверждения соответствия спецификациям стандарта. После чего она станет доступна всем желающим.
Так как OpenCL должен работать поверх некоторой специфической для железа оболочки, а значит для того чтобы можно этот стандарт действительно стал единым для различных гетерогенных систем – надо чтобы соответствующие оболочки (драйверы) были выпущены и для IBM Cell и для Intel Larrabie. Пока от этих гигантов IT ничего не слышно, таким образом OpenCL остается еще одним средством разработки для GPU на ряду с CUDA, Stream и DirectX Compute.
- OpenTK — библиотека-обертка над OpenGL, OpenAL и OpenCL для .Net.
- PyOpenCL – обертка над OpenCL для Pyton.
- Java обертка для OpenCL.
Заключение
Технология OpenCL представляет интерес для различных компания IT сферы – от разработчиков игр до производителей чипов, а это означает что у нее большие шансы стать фактическим стандартом для разработки высокопроизводительных вычислений, отобрав этот титул у главенствующей в этом секторе CUDA.
В будущем я планирую более подробную статью о самом OpenCL, описывающую что из себя представляет эта технология, ее особенности, достоинства и недостатки.
Спасибо за внимание.
Источник: habr.com
Intel sdk for opencl что это за программа
Комментарии пользователей
Представлена версия для Linux
20.11.2013 10:41
Все программы в каталоге — это программы, которые установлены у наших пользователей на компьютерах. Информация о них обновляется автоматически и НЕ проверяется. Наша компания не имеет к данным программам никакого отношения (исключением, разумеется, является WinTuning), мы лишь предоставляем информацию о них. Следовательно, никаких гарантий за чужие программы мы предоставить не можем. Вы можете использовать программы из каталога на свой страх и риск.
Generated in 0.000879 secs.
Источник: oprogramme.ru
Русские Блоги
[Обучение OpenCL] Постоянная настройка среды библиотеки OpenCL (Intel) в Visual Studios
Чтобы написать большую параллельную работу, Xiaobai вчера впервые использовал VS и провел утро, изучая, как настроить OpenCL. _(:з」∠)_
Можно сказать, что этот урок довольно дружелюбен для новичков ~
(1) Проверьте, что конфигурация видеокарты вашего компьютера не поддерживает OpenCL
Мой компьютер использует графическую карту Intel, особый способ просмотра: щелкните правой кнопкой мыши на рабочем столе → Настройки графики Intel → Опции и поддержка
(2) Скачать OpenCL
После того, как я загрузил и установил здесь, путь по умолчанию является C: Intel OpenCL
(3) Настройте среду библиотеки OpenCL
1. Откройте Visual Studio2013 → новый → Консольное приложение Win32 → Именование opencl → определить
2. Откройте страницу свойств
Посмотреть → Другие окна → Управляющий недвижимостью
Щелкните правой кнопкой мыши Microsoft.Cpp.Win32.user в окне диспетчера свойств справа. → Свойства
3. Настройте библиотечные файлы
3.1 Настроить каталог CL
Включите заголовочный файл OpenCL SDK в проект:
C/C++ → генеральный → Дополнительный каталог включения → редактировать → Добавить каталог папок CL
Моя директория C: Intel OpenCL sdk include
3.2 Настройка препроцессора
C/C++ → препроцессор → Определение препроцессора → редактировать → Добавить » _CRT_SECURE_NO_WARNINGS »
Это сделано для того, чтобы избежать сообщения об ошибках определенным функциям во время компиляции.
3.3 Настройка внешнего зависимого каталога OpenCL.lib
Linker → Общие → Дополнительная библиотека → Изменить → Добавить каталог, в котором находится OpenCL.lib
Здесь следует отметить, что каталоги для 32-битных и 64-битных платформ различны.
Мое решение платформы здесь Win32, каталог C:IntelOpenCLsdklibx86
Если это Win64, каталог C: Intel OpenCL sdk lib x64
В то же время установите параметр Включить инкрементную ссылку на Нет:
Linker → Общие → Включить инкрементную ссылку → Изменить → нет
3.4 Настройка файла OpenCL.lib
Linker → Введите → Дополнительные зависимости → Изменить → Добавить «OpenCL.lib»
После настройки щелкните правой кнопкой мыши Microsoft.Cpp.Win32.user → спасти Microsoft.Cpp.Win32.user 。
После успешного сохранения нет необходимости настраивать OpenCL для следующего нового проекта.
(4) Запустите программу HelloWorld с OpenCL
1. Создайте новый пустой проект: файл → Новый → Проект → Visual C++ → пустой проект
2. Создайте два новых файла: файл → новый → Документы → Visual C++ → C ++ файл (.cpp)
Первый — это основной файл функции (содержимое совпадает с приложением 1), сохраненный в формате .cpp.
Второй — это файл функции ядра OpenCL (содержимое совпадает с приложением 2), сохраненный в формате .cl
Здесь основная функция называется source 1.cpp, а ядерная функция называется HelloWorld_Kernel.cl.
3. Добавить исходные файлы
Посмотреть → Обозреватель решений
В маленьком окошке справа
Щелкните правой кнопкой мыши по исходному файлу. → Добавить → Существующие предметы → источник 1.cpp
Щелкните правой кнопкой мыши по исходному файлу. → Добавить → Существующие предметы → HelloWorld_Kernel.cl
4. Скомпилируйте и запустите
в Нажмите «Local Windows Debugger» в исходном файле 1.cpp для компиляции и запуска.
5. Результат операции
Приложение 1 Источник 1.cpp
// OpenCl-1.cpp : Defines the entry point for the console application. // // # include «stdafx.h» Атрибут проекта скомпилированного заголовка c / c ++ уже есть #include #include #include #include #include #include #include using namespace std; #pragma warning (disable: 4996) // Определение макроса, чтобы избежать сообщения об ошибках. // Потому что clCreateCommandQueue () устарела в OpenCL2.0 cl_int ConvertToString(const char *pFileName, std::string int _tmain(int argc, _TCHAR* argv[]) < cl_int iStatus = 0; // функция возвращает статус cl_uint uiNumPlatforms = 0; // количество платформ cl_platform_id Platform = NULL; // выбранная платформа size_t uiSize = 0; // Количество байтов имени версии платформы cl_int iErr = 0; // возвращаем параметр char * pName = NULL; // имя версии платформы cl_uint uiNumDevices = 0; // количество устройств cl_device_id * pDevices = NULL; // устройство cl_context Context = NULL; // среда устройства cl_command_queue CommandQueue = NULL; // Очередь команд const char * pFileName = «HelloWorld_Kernel.cl»; // имя файла cl string strSource = «»; // Используется для хранения кода в файле cl const char * pSource; // указатель строки кода size_t uiArrSourceSize [] = ; // длина строки кода cl_program Program = NULL; // программный объект const char * pInput = «gdkknvnqkc»; // входная строка size_t uiStrlength = strlen (pInput); // длина входной строки char * pOutput = NULL; // выходная строка cl_mem memInutBuffer = NULL; // Входной объект памяти cl_mem memOutputBuffer = NULL; // Выходной объект памяти cl_kernel Kernel = NULL; // Объект ядра size_t uiGlobal_Work_Size [1] = ; // Используется для установки распределения ядра // ——————- 1. Получить и выбрать доступные платформы ———————- ——- // Запрос количества доступных платформ и возвращение статуса iStatus = clGetPlatformIDs(0, NULL, if (CL_SUCCESS != iStatus) < cout // получить адрес платформы if (uiNumPlatforms> 0) // Если есть доступные платформы < // Распределение памяти для платформ в зависимости от количества платформ cl_platform_id *pPlatforms = (cl_platform_id *)malloc(uiNumPlatforms * sizeof(cl_platform_id)); // Получить доступные платформы iStatus = clGetPlatformIDs(uiNumPlatforms, pPlatforms, NULL); Platform = pPlatforms [0]; // получить адрес первой платформы free (pPlatforms); // Освободить пространство памяти, занимаемое платформой >// Получить название версии платформы // Получить количество байтов имени версии платформы iErr = clGetPlatformInfo(Platform, CL_PLATFORM_VERSION, 0, NULL, // Распределение памяти для имени версии платформы в соответствии с количеством байтов pName = (char *)alloca(uiSize * sizeof(char)); // Получить название версии платформы iErr = clGetPlatformInfo(Platform, CL_PLATFORM_VERSION, uiSize, pName, NULL); cout else < pDevices = (cl_device_id *)malloc(uiNumDevices * sizeof(cl_device_id)); iStatus = clGetDeviceIDs(Platform, CL_DEVICE_TYPE_GPU, uiNumDevices, pDevices, NULL); >// ——————- 3. Создать среду устройства ———————— ——— // Создать среду устройства Context = clCreateContext(NULL, 1, pDevices, NULL, NULL, NULL); if (NULL == Context) < cout // ——————- 4. Создание очереди команд ———————— ————— // Создаем очередь команд первого устройства CommandQueue = clCreateCommandQueue(Context, pDevices[0], 0, NULL); if (NULL == CommandQueue) < cout // ———————- 5. Создать программный объект ——————— ——— // Преобразовать код в файле cl в строку iStatus = ConvertToString(pFileName, strSource); pSource = strSource.c_str (); // Получить указатель на strSource uiArrSourceSize [0] = strlen (pSource); // размер строки // Создать программный объект Program = clCreateProgramWithSource(Context, 1, if (NULL == Program) < cout // —————————— 6. Скомпилируйте программу ————— —————— // Скомпилируем программу iStatus = clBuildProgram(Program, 1, pDevices, NULL, NULL, NULL); if (CL_SUCCESS! = iStatus) // ошибка компиляции < cout // ————————- 7. И создать объекты памяти ввода и вывода ядра ————— —————— // Создать объект ввода памяти memInutBuffer = clCreateBuffer( Context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, // Входная память доступна только для чтения и может быть скопирована из памяти хоста в память устройства (uiStrlength + 1) * sizeof (char), // Введите размер пространства памяти (void *)pInput, NULL); // Создать объект памяти вывода memOutputBuffer = clCreateBuffer( Context, CL_MEM_WRITE_ONLY, // Выходная память может быть записана только (uiStrlength + 1) * sizeof (char), // размер памяти на выходе NULL, NULL); if ((NULL == memInutBuffer) || (NULL == memOutputBuffer)) < cout // ————————— 8. Создайте объект ядра —————— ——————— Kernel = clCreateKernel(Program, «helloworld», // Функция ввода в файл cl NULL); if (NULL == Kernel) < cout // —————————- 9. Установить параметры ядра ————— ——————- iStatus = clSetKernelArg(Kernel, 0, // индекс параметра sizeof(cl_mem), (void *) iStatus |= clSetKernelArg(Kernel, 1, sizeof(cl_mem), (void *) if (CL_SUCCESS != iStatus) < cout // ————————— 10. Запуск ядра —————— ————— uiGlobal_Work_Size [0] = uiStrlength; // размер входной строки // Используем очередь команд, чтобы поставить ядро в очередь на устройстве iStatus = clEnqueueNDRangeKernel( CommandQueue, Kernel, 1, NULL, uiGlobal_Work_Size, // Определяем распределение ядра между несколькими процессорами на устройстве NULL, // Определяем распределение ядра между несколькими процессорами на устройстве 0, NULL, NULL); if (CL_SUCCESS != iStatus) < cout // —————————- 11. Считать вывод в память хоста pOutput = (char *) malloc (uiStrlength + 1); // uiStrlength — длина входной строки iStatus = clEnqueueReadBuffer( CommandQueue, // Очередь команд memOutputBuffer, // Вывод объекта памяти CL_TRUE, // Эта функция не вернется до конца чтения ядра 0, uiStrlength * sizeof(char), pOutput, 0, NULL, NULL); if (CL_SUCCESS != iStatus) < cout // ——————— 12 — Результат расчета выхода ————— pOutput[uiStrlength] = ‘