c++ amp:复合类型,for循环,GPU上的声明
Posted
技术标签:
【中文标题】c++ amp:复合类型,for循环,GPU上的声明【英文标题】:c++ amp: compound type, for loop, declaration on GPU 【发布时间】:2015-03-17 23:59:00 【问题描述】:我正在使用 c++ amp 编写模拟代码。我有以下问题,无法为他们找到明确的答案。希望有人能帮忙。实际上,它们是相互关联的。
我写了一些这样的行并且它有效,我已经测试了结果,但我需要有人指出为什么它是对或错的。
class sample
double x;
double y;
bool good;
;
vector<sample> A;
//push_back some values for A
double M[3];
array_view<sample,1> ar(A.size(),A);
array_view<double,1> m(3,M);
double t = 1;
parallel_for_each(ar.extent, [=](index<1> idx) restrict(amp)
double a, b; // Q1
for (int i=0;i<3;i++) //Q2
a += m[i]*ar[idx].x;
b += m[i]*ar[idx].y;
ar[idx].x = a;
ar[idx].y = b;
if ( (a+b) > 1 )
ar[idx].good = 0; //Q3
);
ar.synchronize();
Q1 可以像本例那样在parallel_for_each 中进行声明吗?这些变量有 GPU 作用域吗?如果是这样,在GPU上,这些变量是存储在公共内存空间还是GPU中的每个核心/线程都有自己的本地内存,这样所有计算都不会相互影响?我们甚至可以为临时使用声明一个数组或向量吗?
Q2 是否建议在 parallel_for_each 中使用“for”循环?有什么相关的吗?
Q3 在parallel_for_each 中使用'if',特别是如这里所示,推荐作为某个类的成员?如果有一些缺点,我会在 parralel_for_each 之外进行检查。
基本上这个例子显示了我想要做的事情:我想构建一个类,它会生成一个向量(很多)对象,它们将由一个矩阵更新。我想用c++ amp来加速计算。
我可能在这里包含了太多问题,请对其中任何一个发表评论。先谢谢了。
【问题讨论】:
在外部范围内将a
作为array_view
并在parallel_for 范围内重新声明为double
,这真是令人困惑。
谢谢,@acraig5075。已编辑。您对parallel_for_each 正文中的声明有什么意见吗?其实我想在parallel_for_each里面声明一个数组,因为在我的计算中,向量的每个元素的矩阵都会不同。
【参考方案1】:
Q1: a
和 b
在线程范围内声明。每个线程都有自己的副本。与所有 C++ 变量一样,您应该初始化它们。是的,您可以在此处声明一个数组,但要小心分配大量的每个线程数据。 GPU 具有少量的每线程内存。这可能会限制 GPU 当前能够执行的线程数,从而导致占用率低和性能下降。
Q2:这个for
循环是OK-ish,因为它在每个线程上运行完全相同。请记住,线程意味着分支,如果经线中的不同线程遵循不同的分支,这可能会导致经线发散。您可能想要手动展开此循环,或者编译器可能会为您完成。
Q3:一般情况下,您应该避免在parallel_for_each
内进行分支,因为这会导致warp 发散。这里的循环非常糟糕,因为( (a+b) > 1 )
条件将有线程在每个warp 上评估真假。
您真正应该考虑的另一件事是避免使用结构数组array_view<sample,1>
,因为它会导致糟糕的内存访问模式。最好使用单独的数组来存储x
、y
和good
。这将允许 GPU 合并内存负载。
是的,类/结构更易于使用,但通常会影响性能。根据您的代码库从结构数组 (AoS) 移动到数组结构 (SoA) 的大小,以后可能会很痛苦。在运行时将 AoS 展开为 SoA 以在 GPU 上使用可能也值得避免,因为它需要大量内存副本,这反过来又是一个很大的性能损失。 AoS 工作正常,只需注意对(更大的)代码库可能的性能影响和长期影响。
因为您的循环是固定大小且非常小,您可能需要展开它。如果M
中的值是常量,那么您可能根本不应该将它们存储在全局数组中,而只需对它们进行硬编码。您还可以将if
条件替换为给出相同结果的函数。
示例如下。我没有测试过这个的性能。它可能更快,您应该对其进行基准测试,因为这些只是建议。
#include <amp_math.h>
struct sample
double x;
double y;
bool good;
public:
sample(double x, double y) : x(x), y(y), good(true)
;
main()
std::array<sample, 2> A = sample(0.0000001, 0.0), sample(1.0, 1.0) ;
double M[3] = 1.0, 1.5, 2.0 ;
array_view<sample, 1> ar(A.size(), A);
array_view<double, 1> m(3, M);
double t = 1;
parallel_for_each(ar.extent, [=](concurrency::index<1> idx) restrict(amp)
ar[idx].x = m[0] * ar[idx].x + m[1] * ar[idx].x + m[2] * ar[idx].x;
ar[idx].y = m[0] * ar[idx].y + m[1] * ar[idx].y + m[2] * ar[idx].y;
// WARNING: Single precision!
ar[idx].good = !concurrency::fast_math::signbit(ar[idx].x + ar[idx].y - 1.0);
);
ar.synchronize();
【讨论】:
非常感谢您的回答和建议。尤其是 Q1,GPU 内存模式真的让我很困惑。我读了你的书(C++ AMP)并了解到一组类的效率较低。但是一个类可以将不同类型的数据保存在一起,因此使用起来更简单。如果性能不可接受,我会将它们展开到不同的向量。我理解分支就像 if 会拖累性能,但我真的想标记一些项目并稍后从向量中删除它们。有什么建议或例子吗?以上是关于c++ amp:复合类型,for循环,GPU上的声明的主要内容,如果未能解决你的问题,请参考以下文章