博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++ AMP 概述
阅读量:1971 次
发布时间:2019-04-27

本文共 10776 字,大约阅读时间需要 35 分钟。

C++ 加速的大量并行 (C++ AMP) 加速 C++ 代码的执行利用数据并行的硬件 (如在离散图形卡的图像单元 (GPU)。 使用 C++ AMP,可以编写多维数据算法使用在异类硬件,的并行执行,以便可以加速。 C++ AMP 编程模型包括多维数组、索引,内存调用,平铺和数学函数库。 可以使用 C++ AMP 语言扩展控件数据如何从 CPU 移到 GPU 并返回,因此,可以提高性能。

  • Windows 7、Windows 8、Windows Server 2008 R2 或 Windows Server 2012

  • DirectX 11 功能级别 11.0 或更高硬件

  • 对于调试在软件模拟器,需要 Windows 8 或 Windows Server 2012。 对于调试在硬件,则必须安装您的图形卡的驱动程序。 有关更多信息,请参见。

下面两个示例说明 C++ AMP. 主要元素。 假定,要添加两个一维数组的相应元素。 例如,您可能希望添加 {1, 2, 3, 4, 5} 和 {6, 7, 8, 9, 10} 获取 {7, 9, 11, 13, 15} 不使用 C++ AMP,您可以编写添加数字和显示结果的代码。

#include 
void StandardMethod() { int aCPP[] = {1, 2, 3, 4, 5}; int bCPP[] = {6, 7, 8, 9, 10}; int sumCPP[5]; for (int idx = 0; idx < 5; idx++) { sumCPP[idx] = aCPP[idx] + bCPP[idx]; } for (int idx = 0; idx < 5; idx++) { std::cout << sumCPP[idx] << "\n"; }}

代码的最重要部分如下所示:

  • 数据:该数据包括三个数组。 所有具有相同的秩 (1) 和长度 (5)。

  • 迭代:第一个 for 循环用于重复提供框架通过数组中的元素。要执行计算总和的代码在第一 for 包含块。

  • 索引:idx 变量的访问数组的各个元素。

使用 C++ AMP,您可以编写以下代码。

#include 
#include
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
a(size, aCPP); array_view
b(size, bCPP); array_view
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"; }}

相同的基本元素存在,但是,使用 C++ AMP 构造:

  • 数据:您使用 C++ 数组构造三个 C++ AMP  对象。 您提供四个值构造 array_view 对象:数据值、秩、元素类型和 array_view 对象的长度在每个维度的。 该级别和类型将作为类型参数。 该数据和长度将作为构造函数参数。 在此示例中,传递给构造函数的 C++ 一维数组。 数组的秩和长度用于构造矩形在 array_view 对象的数据,并且,数据值用于填充数组。 c 运行库还包括 ,使用接口本文上类似于 array_view 选件类和稍后对此进行讨论。

  • 迭代: " 用于重复提供框架通过数据元素或 计算字段 在此示例中,的计算字段由 sum.extent指定。 要执行的代码在 lambda 表达式或 核函数包含。 restrict(amp) 指示 C++ AMP 可以加快使用 C++ 语言的子集。

  • 索引: 变量,idx,声明了级别一个匹配 array_view 对象的级别。 通过使用索引,您可以访问 array_view 对象的各个元素。

在运行核心代码之前,必须定义数据值和声明数据的形状。 所有数据被定义为数组 (矩形),因此,您可以定义数组有任何秩 (维数)。 数据可以是中的所有范围任何维度。

Hh265136.collapse_all(zh-cn,VS.110).gif索引选件类

 在 array 或 array_view 对象指定位置。封装从原点的偏移量。每一维到对象中。 当您访问数组的,则一位置传递给索引运算符的一 index 对象,[],而不是整数索引列表。 使用 "或,您可以在每个维度的元素。

下面的示例创建一个一维 array_view 对象指定第三个元素的一维索引。 索引。array_view 对象用于打印第三个元素。 输出为 3。

int aCPP[] = {1, 2, 3, 4, 5};array_view
a(5, aCPP);index<1> idx(2);std::cout << a[idx] << "\n"; // Output: 3

下面的示例创建一 array_view 两维对象指定元素 = 1 行和列 = 2 的两维索引。 在 index 构造函数的第一个参数是行,元素,第二个参数是列元素。 输出为 6。

int aCPP[] = {1, 2, 3,              4, 5, 6};array_view
a(2, 3, aCPP);index<2> idx(1, 2);std::cout << a[idx] << "\n";// Output: 6

下面的示例创建一 array_view 三维对象指定元素深度为 0,= 1 行和列 = 3 的三维索引。 通知第一个参数是深度元素,第二个参数是行元素,因此,第三个参数是列元素。 输出为 8。

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};array_view
a(2, 3, 4, aCPP); // Specifies the element at 3, 1, 0.index<3> idx(0, 1, 3); std::cout << a[idx] << "\n";// Output: 8

Hh265136.collapse_all(zh-cn,VS.110).gif区域选件类

 在 array 或 array_view 对象的每一维指定数据的长度。 可以创建区域并将其用于创建 array 或 array_view 对象。 您也可以检索现有 array 或 array_view 对象的边界。 下面的示例在 array_view 对象的每个维度打印该区域的长度。

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_view
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";

下面的示例创建包含维度和对象与前面示例的一 array_view 对象,但是,此示例使用 extent 对象而不是使用显式参数在 array_view 构造函数。

int aCPP[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};extent<3> e(2, 3, 4);array_view
a(e, 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";

用于的两种数据容器的数据移动到快捷键在运行库中定义。 它们是  和 。 array 选件类是创建数据的一个深层副本容器选件类,当对象构造时。 array_view 选件类是复制数据,当核函数访问数据时的包装选件类。 当数据所需的源计算机时该数据复杂录。

Hh265136.collapse_all(zh-cn,VS.110).gif数组选件类

当 array 对象构造时,数据的一个多层次复制在快捷键后,如果使用包含指向设置的数据的构造函数。 核函数修改在快捷键对应的副本。 当核函数的实现完成时,必须将数据复制到源数据结构。 下面的示例使用在矢量的每个元素是 10。 在核函数完成后, 用于复制数据传回向量对象。

std::vector
data(5);for (int count = 0; count < 5; count++) { data[count] = count;}array
a(5, data.begin(), data.end());parallel_for_each( a.extent, [=, &a](index<1> idx) restrict(amp) { a[idx] = a[idx] * 10; });data = a;for (int i = 0; i < 5; i++) { std::cout << data[i] << "\n";}

Hh265136.collapse_all(zh-cn,VS.110).gifarray_view 选件类

array_view 关闭具有成员和 array 选件类相同,但是,基础行为不相同。 当它是以 array 构造函数,数据传递给构造函数 array_view 在 GPU 不会复制。 相反,那么,当核函数时,将数据复制到快捷键。 因此,因此,如果创建使用相同数据的两个array_view 对象,两个 array_view 对象引用同一内存空间。 如果这样做,则必须同步所有多线程的访问。 使用 array_view 选件类的主要优点是数据移动,只有 + 当是必需的。

Hh265136.collapse_all(zh-cn,VS.110).gif数组和 array_view 比较

下表总结了相似和不同之处。array 和 array_view 选件类之间。

描述

数组选件类

array_view 选件类

当级别确定的

在编译时。

在编译时。

当确定的区域

运行时。

运行时。

形状

矩形。

矩形。

数据存储区

是数据容器。

是数据包装。

复制

在定义的显式和多层次复制。

隐式复制,在核函数访问。

检索数据

通过复制回对象的数组数据在 CPU 请线程。

由 array_view 对象的直接访问或通过调用  "继续访问数据在原始容器。

 函数定义可在快捷键若要在 array 或 array_view 对象数据的代码。 考虑从以下主题中介绍的代码。

#include 
#include
using namespace concurrency;void AddArrays() { int aCPP[] = {1, 2, 3, 4, 5}; int bCPP[] = {6, 7, 8, 9, 10}; int sumCPP[5] = {0, 0, 0, 0, 0}; array_view
a(5, aCPP); array_view
b(5, bCPP); array_view
sum(5, sumCPP); parallel_for_each( sum.extent, [=](index<1> idx) restrict(amp) { sum[idx] = a[idx] + b[idx]; } ); for (int i = 0; i < 5; i++) { std::cout << sum[i] << "\n"; }}

parallel_for_each 方法采用两个参数、计算字段和 lambda 表达式。

计算字段 是 extent 对象或定义设置线程对并行执行创建的 tiled_extent 对象。 一个线程对计算字段的每个元素生成。 在这种情况下,extent 对象一维并有五个元素。 因此,五个线程。

lambda 表达式 在每个线程定义代码运行。 获取子句,[=],指定 lambda 表达式访问的主体都会因值获取了变量,这是 a,b和 sum 在此示例中,参数列表创建名为 idx的一维 index 变量。 idx[0] 的值为 0 在第一个线程并递增一个在每个后续的线程。restrict(amp) 指示 C++ AMP 可以加快使用 C++ 语言的子集。 在具有限制修饰符的功能的限制在 所述。 有关更多信息,请参见 。

lambda 表达式可以包含代码执行也可以调用单独的核函数。 核函数必须包含 restrict(amp) 修饰符。 下面的示例与前面的示例是等效的,但是,它调用单独的核函数。

#include 
#include
using namespace concurrency;void AddElements(index<1> idx, array_view
sum, array_view
a, array_view
b) restrict(amp){ sum[idx] = a[idx] + b[idx];}void AddArraysWithFunction() {
int aCPP[] = {1, 2, 3, 4, 5}; int bCPP[] = {6, 7, 8, 9, 10}; int sumCPP[5] = {0, 0, 0, 0, 0}; array_view
a(5, aCPP); array_view
b(5, bCPP); array_view
sum(5, sumCPP); parallel_for_each( sum.extent, [=](index<1> idx) restrict(amp) { AddElements(idx, sum, a, b); } ); for (int i = 0; i < 5; i++) { std::cout << sum[i] << "\n"; }}

使用平铺,可以获得额外的加速。 平铺部件线程划分为等于矩形子集或 平铺 在代码中您确定适当平铺根据您的数据范围的设置和算法。 对于每个线程,则可以访问元素的 全局 位置相对于整个 array 或 array_view 和访问于 本地 位置相对于平铺。 使用当地,因为您不必编写将全局的索引值的代码对于本地,为简化代码。 若要使用平铺,调用计算字段的  在 parallel_for_each 方法,并使用  对象在 lambda 表达式。

在典型的应用程序,在平铺的元素以某种方式相关,因此,代码必须访问和记录在平铺之间的值。 使用  关键字和  完成此操作。 具有 tile_static 关键字的变量在整个之间的范围平铺和变量的实例对每个创建平铺。 必须平铺线程访问用于处理同步对变量的。  "停止当前线程的执行,直到在平铺的所有线程达到调用 tile_barrier::wait 使用 tile_static 变量,以便可以累积在平铺之间的值。 然后可以执行对所有值的任何计算。

下图表示的采样数据二维数组让平铺。

平铺范围中的索引值

下面的代码示例使用从上图中的采样数据。 代码中值的平均值。平铺的替换在平铺的每个值。

// Sample data:int sampledata[] = {    2, 2, 9, 7, 1, 4,    4, 4, 8, 8, 3, 4,    1, 5, 1, 2, 5, 2,    6, 8, 3, 2, 7, 2};// The tiles:// 2 2    9 7    1 4// 4 4    8 8    3 4//// 1 5    1 2    5 2// 6 8    3 2    7 2// Averages:int averagedata[] = {     0, 0, 0, 0, 0, 0,     0, 0, 0, 0, 0, 0,     0, 0, 0, 0, 0, 0,     0, 0, 0, 0, 0, 0, };array_view
sample(4, 6, sampledata);array_view
average(4, 6, averagedata);parallel_for_each( // Create threads for sample.extent and divide the extent into 2 x 2 tiles. sample.extent.tile<2,2>(), [=](tiled_index<2,2> idx) restrict(amp) { // Create a 2 x 2 array to hold the values in this tile. tile_static int nums[2][2]; // Copy the values for the tile into the 2 x 2 array. nums[idx.local[1]][idx.local[0]] = sample[idx.global]; // When all the threads have executed and the 2 x 2 array is complete, find the average. idx.barrier.wait(); int sum = nums[0][0] + nums[0][1] + nums[1][0] + nums[1][1]; // Copy the average into the array_view. average[idx.global] = sum / 4; });for (int i = 0; i < 4; i++) { for (int j = 0; j < 6; j++) { std::cout << average(i,j) << " "; } std::cout << "\n";}// Output:// 3 3 8 8 3 3// 3 3 8 8 3 3// 5 5 2 2 4 4// 5 5 2 2 4 4

C++ AMP 包括两个数学库。  的双精度库提供双精度功能的支持。 它在硬件还提供对单精度功能支持,不过,双精度支持仍需要。 其符合 。 快捷键必须支持完全双精度。 您可以确定它是否通过检查"的值执行。 快速数学库,空间,包含其他设置数学函数。 这些功能,只支持 float 操作的,在双精度数学库中快速执行,但不是尽可能准确的与。 函数在 <amp_math.h> 标头文件包含,并且所有声明 restrict(amp) 在 <cmath> 标头文件中的函数导入到 fast_math 和 precise_math 命名空间。 restrict 关键字用于区分 <cmath> 版本和 C++ AMP 版本。下面的代码计算底) 的以 10 为底,使用快速方法在计算字段,每个值。

#include 
#include
#include
using namespace concurrency;void MathExample() {
double numbers[] = { 1.0, 10.0, 60.0, 100.0, 600.0, 1000.0 }; array_view
logs(6, numbers); parallel_for_each( logs.extent, [=] (index<1> idx) restrict(amp) { logs[idx] = concurrency::fast_math::log10(logs[idx]); } ); for (int i = 0; i < 6; i++) { std::cout << logs[i] << "\n"; }}

C++ AMP 包括对加速的图形编程模型的图形库。 此库。支持本机映像功能的计算机上仅使用。 方法在  命名空间和在 <amp_graphics.h> 标头文件中。 图形库的关键组件为:

  •  :可以使用纹理选件类创建纹理从内存或文件。 纹理类似于数组,因为它们包含数据,因此,它们类似于容器在标准模板库 (STL) 中有关分配和复制构造。 有关更多信息,请参见。 texture 选件类的模板参数是元素类型与该级别。 该级别可以是 1,2 或 3。 元素类型可以是本文后面将介绍的一个简短的向量类型。

  •  :提供对所有纹理的只读访问。

  •  :定义一个基于 intuintfloatdouble、或 的设置长度为 2,3 和 4 的短的向量类型。

与其他 C++ 库,则 Windows 应用商店 apps 可以使用 C++ AMP。 这些文章位于 apps 描述如何包括 C++ 使用 C++、C#、Visual Basic 或 JavaScript,创建的 AMP 代码:

并发可视化工具包括用于分析 C++ AMP 代码性能支持。 这些文章介绍这些功能:

无符号整数模数和部门带符号整数模数和除法具有得更好的性能。 建议您使用无符号整数,如果可能。

参考

其他资源

转载地址:http://bwypf.baihongyu.com/

你可能感兴趣的文章
AGV自动导引运输车
查看>>
Trie树(字典树)
查看>>
COMP7404 Machine Learing——Logistic Regression
查看>>
COMP7404 Machine Learing——Regularization(参数C)
查看>>
COMP7404 Machine Learing——KNN
查看>>
COMP7404 Machine Learing——SVM
查看>>
COMP7404 Machine Learing——Decision Tree & Random Forests
查看>>
COMP7404 Machine Learing——Hyperparameter Grid Search & Nested Cross-Validation
查看>>
COMP7404 Machine Learing——Confusion Matrix & Precision/Recall/F1
查看>>
COMP7404 Machine Learing——ROC
查看>>
Python量子计算qiskit
查看>>
Python的多线程不是真的多线程(GIL全局解释器锁)
查看>>
Python手动读取MNIST数据集
查看>>
Python手动读取CIFAR-10数据集
查看>>
Pytorch(十一) —— 分布式(多GPU)训练
查看>>
Deeplab v3
查看>>
Pytorch之经典神经网络语义分割(3.2) —— ASPP 空洞空间金字塔池化(atrous spatial pyramid pooling )
查看>>
NLP 之 Perplexity困惑度
查看>>
tensor/矩阵/图片等更换通道,调整size
查看>>
本地和colab 中 改变tensorflow的版本
查看>>