[英]Using thrust to handle vectors in CUDA classes?
我对推力在 c++ 类中的适用性有疑问。 我正在尝试实现一个 class object 接收 (x,y,z) 顶点坐标作为 ver1、ver2 和 ver3。 然后,分配给一个三角形并计算面积和法向量。
但是,我不太明白如何创建 class 的推力矢量。
这是我从文件中读取的顶点坐标,我想将它们发送到 class,它将分配给三角形。 在这里,我们是主要的。
thrust::host_vector<double> dum(start, end); //dum has coordinates and I create
vertices from it.
thrust::host_vector<double> ver1(dum.begin(),dum.begin()+3); //initilizing elements in CPU.
thrust::host_vector<double> ver2(dum.begin()+3,dum.begin()+6);
thrust::host_vector<double> ver3(dum.begin()+6,dum.end());
thrust::device_vector<double> ver1_gpu = ver1; //copying CPU vectors to GPU vectors.
thrust::device_vector<double> ver2_gpu = ver2;
thrust::device_vector<double> ver3_gpu = ver3;
triangle(ver1_gpu, ver2_gpu, ver3_gpu);
在三角形 class 中,我尝试初始化前 3 个元素全为零的 3 个顶点。 由于每个顶点都有 3 个坐标。(x、y 和 z)。 我还初始化了区域和普通变量。
class triangle
{
thrust::device_vector<double>v1(3,0);
thrust::device_vector<double>v2(3,0);
thrust::device_vector<double>v3(3,0);
thrust::device_vector<double>E1(3,0);
thrust::device_vector<double>E2(3,0);
thrust::device_vector<double>E3(3,0);
double normal;
double dummy
double area;
public:
__device__ __host__ triangle(device_vector<double>vert1, device_vector<double>vert2, device_vector<double>vert3)
{
triangle.v1 = vert1;
triangle.v2 = vert2;
triangle.v3 = vert3;
triangle.E1 = vert2 - vert1;
triangle.E2 = vert3 - vert1;
dummy = cross(obj.E2, obj.E1);%% Cross product
triangle.Area = norm(dummy) / 2;
triangle.Normal = dummy / norm(dummy);
}
};
我想在设备中进行所有计算。 我是 cuda 及其图书馆的新手,我知道我在很多地方都错了,但我寻求你的帮助。
下面的代码旨在展示如何通过移动语义(不是 Thrust 特定的)和通过巧妙使用 Thrust 的“花式迭代器”( thrust::transform_iterator
和thrust::zip_iterator
)初始化来避免不必要的复制 C++ class 利用 Thrust 向量来卸载计算。 由于我们的 class 应该一次处理许多三角形以利用现代 GPU 的资源并恢复相关的开销,因此它被命名为Triangles
。 要在 GPU 上这样的带宽受限应用程序中实现良好性能, 合并全局 memory 访问是关键。 实现这一目标的一种直接方法是使用所谓的数组结构 (SoA) 语义,例如
struct SoA_example {
double x[N];
double y[N];
double z[N];
};
而不是结构数组语义,例如
struct Vertex {
double x;
double y;
double z;
}
Vertex AoS_example[N];
这个词汇表与下面使用的 C++ 和 Thrust 容器的名称有些冲突,因为我们的“数组”是thrust::device_vector
而std::array
被用作“结构”。
根据上下文,除了下面显示的构造器之外,还可以想到许多其他构造器,例如处理从主机到设备的数据传输、从文件读取值或处理(主机)AoS 格式的输入。
OPs 问题将dummy
和normal
定义为标量,这在数学上没有意义。 两个向量的叉积是另一个向量。 我在这里更正了这个。
以下代码未经测试但可以编译。
#include <cstddef>
#include <array>
#include <utility>
#include <thrust/device_vector.h>
#include <thrust/iterator/transform_iterator.h>
#include <thrust/iterator/zip_iterator.h>
#include <thrust/zip_function.h>
template <typename T>
struct CrossProduct {
__host__ __device__
T operator()(T a, T b, T c, T d) const {
return a * b - c * d;
}
};
template <typename T>
struct TriangleArea {
__host__ __device__
T operator()(T x, T y, T z) const {
return sqrt(x * x + y * y + z * z) / 2;
}
};
template <typename T>
struct NormalizeUsingArea {
__host__ __device__
T operator()(T &val_x, T &val_y, T &val_z, T area) const {
T norm = 0.5 / area;
val_x *= norm;
val_y *= norm;
val_z *= norm;
}
};
template <typename T>
class Triangles
{
static constexpr int x_dim = 0;
static constexpr int y_dim = 1;
static constexpr int z_dim = 2;
static constexpr int n_dims = 3;
using Container = thrust::device_vector<T>;
using Vectors = std::array<Container, n_dims>;
std::ptrdiff_t n_triangles{};
Vectors v1;
Vectors v2;
Vectors v3;
Vectors E1;
Vectors E2;
// Vectors E3;
// Vectors dummies;
Vectors normals;
Container areas;
// helper functions
auto make_minus_iterator(Vectors &vs_1,
Vectors &vs_2,
int component)
{
return thrust::make_transform_iterator(
thrust::make_zip_iterator(
thrust::make_tuple(vs_1[component].cbegin(),
vs_2[component].cbegin())),
thrust::make_zip_function(thrust::minus<T>{}));
}
template <int component>
auto choose_crossprod_components(Vectors &vs_1,
Vectors &vs_2)
{
static_assert(component >= x_dim && component < n_dims);
if constexpr (component == x_dim)
{
return thrust::make_tuple(vs_1[y_dim].cbegin(),
vs_2[z_dim].cbegin(),
vs_1[z_dim].cbegin(),
vs_2[y_dim].cbegin());
}
if constexpr (component == y_dim)
{
return thrust::make_tuple(vs_1[z_dim].cbegin(),
vs_2[x_dim].cbegin(),
vs_1[x_dim].cbegin(),
vs_2[z_dim].cbegin());
}
if constexpr (component == z_dim)
{
return thrust::make_tuple(vs_1[x_dim].cbegin(),
vs_2[y_dim].cbegin(),
vs_1[y_dim].cbegin(),
vs_2[x_dim].cbegin());
}
}
template <int component>
auto make_crossprod_iterator(Vectors &vs_1,
Vectors &vs_2)
{
return thrust::make_transform_iterator(
thrust::make_zip_iterator(choose_crossprod_components<component>(vs_1, vs_2)),
thrust::make_zip_function(CrossProduct<T>{}));
}
auto make_area_iterator(Vectors &crossproduct)
{
return thrust::make_transform_iterator(
thrust::make_zip_iterator(
thrust::make_tuple(crossproduct[x_dim].cbegin(),
crossproduct[y_dim].cbegin(),
crossproduct[z_dim].cbegin())),
thrust::make_zip_function(TriangleArea<T>{}));
}
auto make_zip_iterator(Vectors &vecs,
const Container &scalars)
{
return thrust::make_zip_iterator(
thrust::make_tuple(vecs[x_dim].begin(),
vecs[y_dim].begin(),
vecs[z_dim].begin(),
scalars.cbegin()));
}
public:
// The following constructor is just an example based on OPs constructor.
// Use fancy iterators to avoid unnecessary initialization to 0 of E1, E2, ...
// Depending on the use case it might make more sense to just have the iterators as members
// and compute E1, E2, etc on the fly when needed and get rid of their Vectors members (kernel fusion).
Triangles(thrust::device_vector<T> &&vert1_x,
thrust::device_vector<T> &&vert1_y,
thrust::device_vector<T> &&vert1_z,
thrust::device_vector<T> &&vert2_x,
thrust::device_vector<T> &&vert2_y,
thrust::device_vector<T> &&vert2_z,
thrust::device_vector<T> &&vert3_x,
thrust::device_vector<T> &&vert3_y,
thrust::device_vector<T> &&vert3_z) :
n_triangles{static_cast<std::ptrdiff_t>(vert1_x.size())},
// move device_vectors with vertices into class (avoids expensive copies)
v1{std::move(vert1_x),
std::move(vert1_y),
std::move(vert1_z)},
v2{std::move(vert2_x),
std::move(vert2_y),
std::move(vert2_z)},
v3{std::move(vert3_x),
std::move(vert3_y),
std::move(vert3_z)},
// calculate diffs and initialize E1, E2 with them
E1{Container(make_minus_iterator(v2, v1, x_dim),
make_minus_iterator(v2, v1, x_dim) + n_triangles),
Container(make_minus_iterator(v2, v1, y_dim),
make_minus_iterator(v2, v1, y_dim) + n_triangles),
Container(make_minus_iterator(v2, v1, z_dim),
make_minus_iterator(v2, v1, z_dim) + n_triangles)},
E2{Container(make_minus_iterator(v3, v1, x_dim),
make_minus_iterator(v3, v1, x_dim) + n_triangles),
Container(make_minus_iterator(v3, v1, y_dim),
make_minus_iterator(v3, v1, y_dim) + n_triangles),
Container(make_minus_iterator(v3, v1, z_dim),
make_minus_iterator(v3, v1, z_dim) + n_triangles)},
// calculate cross-products and initialize normals with them(normalize later)
normals{Container(make_crossprod_iterator<x_dim>(E2, E1),
make_crossprod_iterator<x_dim>(E2, E1) + n_triangles),
Container(make_crossprod_iterator<y_dim>(E2, E1),
make_crossprod_iterator<y_dim>(E2, E1) + n_triangles),
Container(make_crossprod_iterator<z_dim>(E2, E1),
make_crossprod_iterator<z_dim>(E2, E1) + n_triangles)},
// calculate areas and initialize with them
areas(make_area_iterator(normals),
make_area_iterator(normals) + n_triangles)
{
// normalize normals
thrust::for_each_n(make_zip_iterator(normals, areas),
n_triangles,
thrust::make_zip_function(NormalizeUsingArea<double>{}));
}
};
// expicit instantiation to find compilation errors on godbolt.com
template class Triangles<double>;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.