【C++学习】GPU编程的简单学习

GPU是什么

显卡的处理器称为图形处理器(GPU),它是显卡的“心脏”,与CPU类似,只不过GPU是专为执行复杂的数学和几何计算而设计的,这些计算是图形渲染所必需的。某些最快速的GPU集成的晶体管数甚至超过了普通CPU。

GPU 已经发展到成熟阶段,可轻松执行现实生活中的各种应用程序,而且程序运行速度已远远超过使用多核系统时的情形。 未来计算架构将是并行核心 GPU 与多核 CPU 共同运行的混合型系统。

GPU编程语言的选择,这里有篇文章讲的十分详细。

我总结了下

  • C++ AMP:只能在windows平台使用,易编程。
  • CUDA:Nvdia上使用,注重性能。
  • OpenCL:注重多平台,可移植性。

我接下来的学习也是C++AMP为主。
C++AMP

AMP全名 Accelerated Massive Parallelism(加速大规模并行计算),是微软提出的基于C++的异构化并行计算平台

先通过一个小例子简单的学习下AMP的使用。

#include 
// 需要引用
#include   
// AMP模版的命名空间
using namespace concurrency; 

const int size = 5;

void CppAmpMethod() {
    int aCPP[] = {1, 2, 3, 4, 5};
    int bCPP[] = {6, 7, 8, 9, 10};
    int sumCPP[size];

    // Create C++ AMP objects.
    array_view<const int, 1> a(size, aCPP);
    array_view<const int, 1> b(size, bCPP);
    array_view<int, 1> sum(size, sumCPP);
    sum.discard_data();

    parallel_for_each( 
        // Define the compute domain, which is the set of threads that are created.
        sum.extent, 
        // Define the code to run on each thread on the accelerator.
        [=](index<1> idx) restrict(amp)
    {
        sum[idx] = a[idx] + b[idx];
    }
    );

    // Print the results. The expected output is "7, 9, 11, 13, 15".
    for (int i = 0; i < size; i++) {
        std::cout << sum[i] << "\n";
    }
}

array_view类

template <
   typename _Value_type,  // array_view 对象中的元素的数据类型。
   int _Rank              // array_view 对象的秩。所谓的秩指的是维度,如果省略则为1。
>

There are three remote usage scenarios:
1. A view to a system memory pointer is passed through a parallel_for_each call to an accelerator and accessed on the accelerator.
2. A view to an accelerator-residing array is passed using a parallel_for_each to another accelerator_view and is accessed there.
3.A view to an accelerator-residing array is accessed on the CPU.

总结下来就是,加速器与加速器,加速器与cpu,加速器与系统内存都要通过array_view类。
array_view类相当于一个接口,一个连接。

sum.discard_data();

discard_data
Often, your algorithm may need to use an array_view purely as an output, and the current contents of the array_view are inconsequential. When accessing such an output array_view on any accelerator_view for a computation, copying the current contents underlying the array_view to the accelerator is undesirable. C++ AMP provides the discard_data method as a means for you to indicate to the runtime that the contents of portion of data underlying the array_view are not interesting and need not be copied when the array_view is accessed on an accelerator_view where the array_view is not already cached. Calling this method on an array_view can be thought of as trashing the existing contents of the portion of data referenced by the array_view and applies to the array_view and all other array_views that are its sections or projections.
也许你的算法需要使用一个array_view 作为一个输出,且当前array_view 的内容是不重要的,访问一个用于输出的array_view对于任何加速器来说都是不好的。C++AMP提供了discard_data(丢弃数据)的方法指明当运行的时候,数据的一部分内容对array_view 来说是不重要的,不需要将其拷贝到加速器中当array_view正在被加速器访问的时候,不缓存它。呼叫array_view的这个方法能够被认为是抛弃array_view中的存在的部分数据内容,适用于array_view 和所有的array_views,巴拉巴拉(最后一句看不懂)。

自己翻译的,不知道对不对,有问题,望指出。
总而言之就是一句话,如果array_view的对象没有必要拷贝进加速器中的时候就用discard_data。
比如sum,它作为输出的容器,最开始里面的内容并不重要,不需要拷贝进加速器中。

array_view < const int, 1>
加了const就表明这个对象是只读的,我们并不会去修改它里面的内容。当然这部分内容也是无法抛弃的。

parallel_for_each 函数 (C++ AMP)具体参数(msdn)

当 parallel_for_each 运行时,计算域中针对每个索引运行一个并行活动。
通常在 GPU 计算机上运行。

parallel_for_each其中一个语法
_template <
int _Rank,
typename _Kernel_type

让我们看下第一个参数 sum.extent

表示指定原始为 0 的 N 维空间边界的 N 个整数值的矢量。 矢量中的值按最重要到最不重要的顺序排列。
看一个例子,msdn的

int aCPP[] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
// There are 3 rows and 4 columns, and the depth is two.
array_view3> a(2, 3, 4, aCPP); 
std::cout << "The number of columns is " << a.extent[2] << "\n";
std::cout << "The number of rows is " << a.extent[1] << "\n";
std::cout << "The depth is " << a.extent[0]<< "\n";
std::cout << "Length in most significant dimension is " << a.extent[0] << "\n";

建立一个3行4列深度为2的数组,通过extent[n]得到每一维度的长度。
即指定了GPU的线程的数量。

在lambda后面有restrict(amp),在amp上的约束。
对其的使用有很多约束条件

A function that has the restrict(amp) clause has the following limitations:
一个函数有restrict(amp)条款需要遵守以下限制
The function can call only functions that have the restrict(amp) clause.
一个函数只能被其他也拥有restrict(amp)条款的函数呼叫
The function must be inlinable.
函数必须是可以内联的
The function can declare only int, unsigned int, float, and double variables, and classes and
structures that contain only these types. bool is also allowed, but it must be 4-byte-aligned if you use it in a compound type.
这个函数只能被int, unsigned int, float, and double variables, and classes and
structures(包含其他类型)声明,bool也可以。如果是符合类型,那必须是四字节对齐。
Lambda functions cannot capture by reference and cannot capture pointers.
拉姆达函数不能捕获任何引用和this指针
References and single-indirection pointers are supported only as local variables, function arguments, and return types.
引用和间接指针只能被本地变量,函数参数和返回类型支持
The following are not allowed:
以下不被允许
Recursion.
递归(天= =为啥不让使用递归)
Variables declared with the volatile keyword.
易变关键词的参数声明
Virtual functions.
虚函数
Pointers to functions.
函数指针
Pointers to member functions.
结构体指针
Pointers in structures.
指针的指针
Pointers to pointers.
剩下的很容易看懂,我就不打了。
goto statements.
Labeled statements.
try, catch, or throw statements.
Global variables.
Static variables. Use tile_static Keyword instead.
dynamic_cast casts.
The typeid operator.
asm declarations.
Varargs.

再来看lambda中的参数

[=](index<1> idx)

= : 获得外部参数拷贝
index<1>: 参数指一维数组,要和array_view申请的维度一致。
idx : 索引->线程。线程的索引

真的是相当容易实现啊,我觉得,至少在windows平台上使用GPU编程是最好的。

你可能感兴趣的:(C++开发)