Следующие темы показывают расширенные возможности библиотеки Boost Compute.
В дополнение к встроенным скалярным типам (например,<int
>и<float
>), OpenCL также предоставляет векторные типы данных (например,<int2
>и<vector4
>). Они могут использоваться с библиотекой Boost Compute как на хосте, так и на устройстве.
Повышаю. Вычисление предоставляет типдефы для этих типов, которые принимают форму:<boost::compute::scalarN_
>, где<scalar
>является скалярным типом данных (например,<int
>,<float
>,<char
>) и<N
>является размером вектора. Поддерживаемые векторные размеры: 2, 4, 8 и 16.
Следующий пример показывает, как передать набор 3D точек, хранящихся в виде массива<float
>s на хосте устройства, а затем вычислить сумму координат точек с помощью функции<accumulate()
>. Сумма переносится на хост и вычисляется центроидом путем деления на общее количество точек.
Обратите внимание, что даже если точки находятся в 3D, они хранятся как<float4
>из-за требований выравнивания OpenCL.
#include <iostream>
#include <boost/compute/algorithm/copy.hpp>
#include <boost/compute/algorithm/accumulate.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/types/fundamental.hpp>
namespace compute = boost::compute;
int main()
{
using compute::float4_;
compute::device device = compute::system::default_device();
compute::context context(device);
compute::command_queue queue(context, device);
float points[] = { 1.0f, 2.0f, 3.0f, 0.0f,
-2.0f, -3.0f, 4.0f, 0.0f,
1.0f, -2.0f, 2.5f, 0.0f,
-7.0f, -3.0f, -2.0f, 0.0f,
3.0f, 4.0f, -5.0f, 0.0f };
compute::vector<float4_> vector(5, context);
compute::copy(
reinterpret_cast<float4_ *>(points),
reinterpret_cast<float4_ *>(points) + 5,
vector.begin(),
queue
);
float4_ sum = compute::accumulate(
vector.begin(), vector.end(), float4_(0, 0, 0, 0), queue
);
float4_ centroid;
for(size_t i = 0; i < 3; i++){
centroid[i] = sum[i] / 5.0f;
}
std::cout << "centroid: " << centroid << std::endl;
return 0;
}
Среда выполнения OpenCL и библиотека Boost Compute обеспечивают ряд встроенных функций, таких как sqrt() и dot(), но во многих случаях этого недостаточно для решения проблемы.
Библиотека Boost Compute предоставляет несколько различных способов создания пользовательских функций, которые могут быть переданы предоставленным алгоритмам, таким как<transform()
>и<reduce()
>.
Самый простой способ - предоставить исходный код для функции:
boost::compute::function<int (int)> add_four =
boost::compute::make_function_from_source<int (int)>(
"add_four",
"int add_four(int x) { return x + 4; }"
);
boost::compute::transform(input.begin(), input.end(), output.begin(), add_four, queue);
Это также можно сделать более кратко, используя макрос<BOOST_COMPUTE_FUNCTION
>:
BOOST_COMPUTE_FUNCTION(int, add_four, (int x),
{
return x + 4;
});
boost::compute::transform(input.begin(), input.end(), output.begin(), add_four, queue);
Также см.«Таможенные функции OpenCL на C++ с Boost.Compute»для более подробной информации.
Повышаю. Вычисление обеспечивает макрос<BOOST_COMPUTE_ADAPT_STRUCT
>, который позволяет обертывать и использовать структуру / класс C++ в OpenCL.
Хотя сама OpenCL изначально не поддерживает сложные типы данных, библиотека Boost Compute предоставляет их.
Для использования комплексных значений сначала включите следующий заголовок:
#include <boost/compute/types/complex.hpp>
Вектор комплексных значений может быть создан таким образом:
boost::compute::vector<std::complex<float> > vector;
vector.push_back(std::complex<float>(1.0f, 3.0f));
vector.push_back(std::complex<float>(2.0f, 4.0f));
Структура выражения лямбда позволяет определять функции и предикаты на сайте вызова алгоритма.
Ламбда-выражения используют заполнители<_1
>и<_2
>для обозначения аргументов. Следующие декларации приведут держателей лямбда в текущую область применения:
using boost::compute::lambda::_1;
using boost::compute::lambda::_2;
Следующие примеры показывают, как использовать выражения лямбда вместе с Boost. Вычислите алгоритмы для выполнения более сложных операций на устройстве.
Чтобы подсчитать число нечетных значений в векторе:
boost::compute::count_if(vector.begin(), vector.end(), _1 % 2 == 1, queue);
Умножить каждое значение в векторе на три и вычесть четыре:
boost::compute::transform(vector.begin(), vector.end(), vector.begin(), _1 * 3 - 4, queue);
Выражения лямбда также могут использоваться для создания функций и объектов:
boost::compute::function<int(int)> add_four = _1 + 4;
Основным узким местом производительности в приложениях GPGPU является передача памяти. Это может быть облегчено путем перекрытия передачи памяти с вычислениями. Библиотека Boost Compute обеспечивает функцию<copy_async()
>, которая выполняет асинхронную передачу памяти между хостом и устройством.
Например, инициировать копию с хоста на устройство и затем выполнять другие действия:
std::vector<float> host_vector = ...
boost::compute::vector<float> device_vector(host_vector.size(), context);
boost::compute::future<void> f = boost::compute::copy_async(
host_vector.begin(), host_vector.end(), device_vector.begin(), queue
);
f.wait();
boost::compute::sort(device_vector.begin(), device_vector.end(), queue);
Например, для измерения времени копирования вектора данных с хоста на устройство:
#include <vector>
#include <cstdlib>
#include <iostream>
#include <boost/compute/event.hpp>
#include <boost/compute/system.hpp>
#include <boost/compute/algorithm/copy.hpp>
#include <boost/compute/async/future.hpp>
#include <boost/compute/container/vector.hpp>
namespace compute = boost::compute;
int main()
{
compute::device gpu = compute::system::default_device();
compute::context context(gpu);
compute::command_queue queue(
context, gpu, compute::command_queue::enable_profiling
);
std::vector<int> host_vector(16000000);
std::generate(host_vector.begin(), host_vector.end(), rand);
compute::vector<int> device_vector(host_vector.size(), context);
compute::future<void> future = compute::copy_async(
host_vector.begin(), host_vector.end(), device_vector.begin(), queue
);
future.wait();
boost::chrono::milliseconds duration =
future.get_event().duration<boost::chrono::milliseconds>();
std::cout << "time: " << duration.count() << " ms" << std::endl;
return 0;
}
Библиотека Boost Compute предназначена для легкого взаимодействия с OpenCL API. Все завернутые классы имеют операторы преобразования в свои основные типы OpenCL, что позволяет передавать их непосредственно в функции OpenCL.
Например,
boost::compute::context ctx = boost::compute::default_context();
cl_uint num_devices;
clGetContextInfo(ctx, CL_CONTEXT_NUM_DEVICES, sizeof(cl_uint), &num_devices, 0);
std::cout << "num_devices: " << num_devices << std::endl;