C++ AMP 介绍(两)
C++ AMP 介绍(两)
最后更新:2014-05-02
读前提:《C++ AMP介绍(一个)》
周边环境:Windows 8.1 64bit英文版,Visual Studio 2013 Update1英文版,Nvidia QuadroK600 显卡
内容简单介绍
介绍C++ AMP的 array、array_view、extent类和平铺的知识。
正文
数据的移动
array和 array_view两个数据容器(模板类)用于把数据从执行时库(CPU)移到加速器(显卡或通用计算卡)上。array类在构造时建立数据的深拷贝。把数据拷贝到加速器(GPU)上,而array_view类是个包装类。只当核心函数(kernel function)要用到数据时。才把源数据拷贝到加速器上。
#include <amp.h>
using namespace concurrency; //演示array类的使用方式
void test_array()
{
//測试数据
std::vector<int> data(5);
for (int count = 0; count < 5; count++)
{
data[count] = count;
} //构造array实例
array<int, 1> a(5, data.begin(), data.end()); parallel_for_each(
a.extent,
[=, &a](index<1> idx) restrict(amp)
{
a[idx] = a[idx] * 10;
}
); //array实例a不须要调用同步方法
//可是须要赋值给data
data = a; //输出0,10,20,30,40
for (int i = 0; i < 5; i++)
{
std::cout << data[i] << "\n";
}
}
array_view同array之间差点儿有同样的成员,可是它们底层的行为不一样,所以当你建立两个指向同一个数据源的array_view实例时,实际上它们指向同一个内存地址。数据仅仅有当须要的时候才会被拷贝到加速器中。所以你得注意数据的同步,array_view类的主要优点是数据仅当要被加速器用到的时候才会被移动。
共享内存是能被CPU和GPU訪问的内存,array类能够控制共享内存的存取方式,可是首先我们须要測试加速器是不是支持共享内存,以下是array使用共享内存的演示样例代码。
int test_sharedMemory()
{
// 一台计算机中可能有多块加速器,取默认加速器
accelerator acc = accelerator(accelerator::default_accelerator); // 測试默认加速器是否支持共享内存
if (!acc.supports_cpu_shared_memory)
{
std::cout << "The default accelerator does not support shared memory" << std::endl;
return 1;
} // 设置cpu默认存取方式
acc.set_default_cpu_access_type(access_type_read_write); //为acc加速器建立accelerator_view(加速器视图)实例
//读写方式默觉得加速器default_cpu_access_type属性的设定
accelerator_view acc_v = acc.default_view; // extent指示array实例建立一个含10个元素的一维数组
extent<1> ex(10); // 指定加速器视图。输入数组在CPU上仅仅写
array<int, 1> arr_w(ex, acc_v, access_type_write); // 指定加速器视图。输出数组在CPU上仅仅读
array<int, 1> arr_r(ex, acc_v, access_type_read); // 指定加速器视图。能够在CPU上读写的数组
array<int, 1> arr_rw(ex, acc_v, access_type_read_write); return 0;
}
index 类
index类指定元素在array或array_view对象中的位置,以下是index类的使用演示样例代码
void test_indexClass()
{
int aCPP[] = { 1, 2, 3,
4, 5, 6 };
//新建2维(两行三列)array_view包装器
array_view<int, 2> a(2, 3, aCPP);
index<2> idx(1, 2); //输出6
std::cout << a[idx] << "\n";
}
extent类
尽管extent类在非常多场合下不是必要的,可是微软的部分演示样例代码使用到了extent class。所以有必要介绍下extent class。
extentclass用来指定array或array_view各个维度的元素数量,你能够使用extent class建立array或array_view对象,也能够从array或array_view对象中存取extent,以下的样例演示了extent class的使用。
void test_extentClass()
{
int aCPP[] = { 111, 112, 113, 114,
121, 122, 123, 124,
131, 132, 133, 134, 211, 212, 213, 214,
221, 222, 223, 224,
231, 232, 233, 234 };
extent<3> e(2, 3, 4);
array_view<int, 3> a(e, aCPP); //断言extent[0],[1],[2]的属性分别为2、3、4
assert(2 == a.extent[0]);
assert(3 == a.extent[1]);
assert(4 == a.extent[2]);
}
parallel_for_each函数
我们在上篇文章中调用过parallel_for_each函数,它有两个入口參数,第一个入口參数为计算域,是个extent或tiled_extent对象。定义了要在加速器上并发执行的线程集合。它会为每一个元素生成一根用于计算的线程。第二个參数是lambda表达式,定义了要在每根线程上执行的代码。
加速代码: 砖面(Tiles )和边界(Barriers)
将全体线程划分为若干个具有相等数量矩形(M*N根)线程集合,每一个集合称为tile(砖面)。多个tile(砖面)组成全体线程。叫做平铺(tiling)。
若要使用平铺,在parallel_for_each 方法中的计算域上调用 extent::tile 方法。并在 lambda 表达式中使用 tiled_index 对象。
以下是两张来自微软官网的砖面(tile)的组织图,能够看到怎样索引元素。
图中的idx是index类。sample是全局空间(array或array_view对象)
下图中的t_idx是index类,descriptions是全局空间(array或array_view对象)
以下这个来自微软官方的样例。每2*2=4根线程组成一个砖面(tile),计算砖面(tile)中元素的平均值。
void test_tile()
{
// 測试样本:
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(以下是6个砖面):
// 2 2 9 7 1 4
// 4 4 8 8 3 4
//
// 1 5 1 2 5 2
// 6 8 3 2 7 2 // averagedata用来存放运算结果:
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,
}; //每四个元素(四根线程)组成一个tile(砖面),所以共同拥有六个tile(砖面)
array_view<int, 2> sample(4, 6, sampledata);
array_view<int, 2> average(4, 6, averagedata); //通过[1]extent.tile取代extent[2]tiled_index取代index,启用平铺模式
parallel_for_each(
// 把extent切分为以2*2为单位的tile(砖面)
sample.extent.tile<2, 2>(),
[=](tiled_index<2, 2> idx) restrict(amp)
{
//tile_statickeyword的变量范围是整个tile(砖面)
//所以每一个tile(砖面)(2*2=4根线程)仅仅实例化一个tile_static
tile_static int nums[2][2]; //tile(砖面)中的全部线程分别执行以下的代码
//把值拷贝到tile_static实例nums,所以同一个nums会被赋值2*2=4次
nums[idx.local[1]][idx.local[0]] = sample[idx.global]; //等待tile(砖面)中的全部线程,执行完上面这段代码
idx.barrier.wait(); //如今nums中的2*2=4个元素已经有有效值了
//tile(砖面)中的全部线程再次分别执行以下的代码 //计算平均值,
int sum = nums[0][0] + nums[0][1] + nums[1][0] + nums[1][1]; //把计算结果拷贝到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
}
使用平铺的优点是,从tile_static变量存取数据要比从全局空间(array和array_view对象)要快。
为了从平铺中得到性能优势,我们的算法必须把计算域拆分为tile(砖面)然后把数据放到tile_static变量中加快数据存取速度。
注意不要使用类似以下的代码来累加tile(砖面)中的数据,
tile_static float total;
total += matrix[t_idx];‘
原因[1]total的初始值是不确定的,所以第二句代码的运算没有意义。
原因[2]因为tile(砖面)中的多根线程竞争同一个title_static变量,计算结果会不确定。
内存屏障(MemoryFences)
在restrict(amp)限定中,有两种内存必需要同步:
全局内存:array或array_view实例
tile_static内存:tile(砖面)内存
内存屏障确保两种内存的线程同步,要调用内存屏障能够使用以下三种方法:
tile_barrier::wait(或tile_barrier::wait_with_all_memory_fence)方法: 建立全局内存和tile_static内存的屏障。
tile_barrier::wait_with_global_memory_fence方法 : 仅建立全局内存的屏障
tile_barrier::wait_with_tile_static_memory_fence 方法 :仅建立tile_static内存的屏障
调用特定类型的屏障(fence)能够提高你应用的性能,在以下的样例中 tile_barrier::wait_with_tile_static_memory_fence 方法的调用取代tile_barrier::wait方法的调用提高了应用的性能。
// 使用tile_static内存屏障
parallel_for_each(matrix.extent.tile<SAMPLESIZE, SAMPLESIZE>(),
[=, &averages](tiled_index<SAMPLESIZE, SAMPLESIZE> t_idx) restrict(amp)
{
// 把数据从全局内存中拷贝到title_static内存中.
tile_static floattileValues[SAMPLESIZE][SAMPLESIZE];
tileValues[t_idx.local[0]][t_idx.local[1]] = matrix[t_idx]; // 等待title_static内存中的数据复制完成
t_idx.barrier.wait_with_tile_static_memory_fence(); // 假设你移除if语句。代码会把被tile(砖面)中的全部线程调用。这样每一个
// tile(砖面)中的元素都会被分配一个相同的平均值.
if (t_idx.local[0] == 0&& t_idx.local[1] == 0) {
for (int trow = 0; trow <SAMPLESIZE; trow++) {
for (int tcol = 0; tcol< SAMPLESIZE; tcol++) {
averages(t_idx.tile[0],t_idx.tile[1]) += tileValues[trow][tcol];
}
}
averages(t_idx.tile[0],t_idx.tile[1]) /= (float) (SAMPLESIZE *SAMPLESIZE);
}
});
restrict(amp)修饰的代码段是在加速器(GPU)上执行的。默认在里面的代码段。下断点不会Break(进入)。在[Solution Explorer]窗体中点击项目名称。快捷键[Alt]+[Enter],打开当前项目属性页,[Configuration Properties]->[Debugging]->[Debugger Type]默觉得“Auto”。改为“GPU Only”就能够Debug当前项目加速器(GPU)上执行的代码了。
据官网介绍。无符号整数的处理速度要比带符号整数快。所以尽量用无符号整数吧。
參考资料
《Using Tiles》
http://msdn.microsoft.com/en-us/library/vstudio/hh873135.aspx
《使用平铺》
http://msdn.microsoft.com/zh-cn/library/vstudio/hh873135.aspx
版权声明:本文博客原创文章,博客,未经同意,不得转载。
C++ AMP 介绍(两)的更多相关文章
- 介绍两个Eclipse插件: Implementors & Call Hierarchy
介绍两个Eclipse插件: Implementors & Call Hierarchy 本文介绍两个在Eclipse调试与跟踪过程中的两个实用插件 他们都可以在 http://eclipse ...
- 介绍两款Linux文件恢复工具,ext3grep与extundelete https://www.cnblogs.com/lazyfang/p/7699994.html
介绍两款Linux文件恢复工具,ext3grep与extundelete,可能在关键时刻会有所帮助.ext3grep仅对ext3文件系统有效,extundelete对ext3与ext4文件系统都有效 ...
- 介绍两款常用的“图表统计图"的插件
一.相信朋友们在开发的过程中都会使用到“数据统计”的功能,图表的统计更为直观,在这里就介绍两款插件:fusionChart.DataVisualization. 1.fusionChart实际项目中用 ...
- 介绍两种在RHEL 和 CentOS 系统上检查或列出已安装的安全更新的方法
在本文中,我们将向你展示如何检查已安装的安全更新.我会介绍两种方法,你可以选择最适合你的. 此外,我还添加了一个小的 shell 脚本,它为你提供已安装的安全包计数. 运行以下命令获取系统上已安装的安 ...
- 【推荐】介绍两款Windows资源管理器,Q-Dir 与 FreeCommander XE(比TotalCommander更易用的免费资源管理器)
你是否也像我一样,随着硬盘.文件数量的增加,而感到对于文件的管理越来越乏力. 于是我试用了传说中的各种软件,包括各种Explorer外壳,或者第三方资源管理器. 最后我确定下来经常使用,并推荐给您的是 ...
- MES实施会有哪些情况?为你介绍两种常见的类型
MES项目实施顾问是一份极具挑战的工作,需具备大量的专业知识,以及丰富的实施经验.今天,小编为大家介绍最常见的两种MES实施顾问类型,希望对大家有所启发. 保姆型实施顾问 是指以实施顾问为主导,只要是 ...
- 介绍两种风格的URL
两种风格的“动态资源”——统一资源定位符(Uniform Resource Lactor,URL) 当前互联网上主要有两种主要风格的URL: 第一种直接在URL中知名文件(比如xxx.php,xxx. ...
- 介绍两个Android不常用的Drawable:GradientDrawable和 StateListDrawable
//-------------------------------------------------------------------------------------------------- ...
- 介绍两个非常好用的Javascript内存泄漏检测工具
内存泄漏对开发者来说一般很难检测因为它们是由一些大量代码中的意外的错误引起的,但它在系统内存不足前并不影响程序的功能.这就是为什么会有人在很长时间的测试期中收集应用程序性能指标来测试性能. 最简单的检 ...
- 介绍两个挺好用的基于Jquery的上传工具
一.ajaxFileUpload 二.fineUploader
随机推荐
- 代理下载android4.4源代码
前提条件:需要有user, password, 代理人serverip和port(这一切都使自己的软件来完成下一个.例如freegate,它拥有一套可以自己作为一个代理server.创user/pas ...
- opencv-形态处理
开运算 (Opening) 原理摘自:http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/opening_closi ...
- 折返(Reentrancy)VS线程安全(Thread safety)
在Wiki上,折返例如,下面的定义(接) In computing, a computer program or subroutine is called reentrant if it can be ...
- 依据Uri获得sd卡图片
<pre name="code" class="java">String mBigImage = URLEncoder.encode(mImageU ...
- 这个夏天不AC(杭州电2037)
这个夏天不AC Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Su ...
- 页面中插入百度地图(使用百度地图API)
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWF5dW4wNTE2/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...
- 十依据一个有用的算法来找到最小(最大)的k的数量-线性搜索算法
例如:进入1.2.3,4,5,6.7.8此8数字,最小的4图的1,2,3,4. 思路1:最easy想到的方法:先对这个序列从小到大排序.然后输出前面的最小的k个数就可以.假设选择高速排序法来进行排序, ...
- VB.NET之错误异常处理
相对于VB而言,VB.NET中引入了很多特色.当中最吸引我的就是引入了结构化异常处理. 尽管VB.NET仍然支持OnError Goto类型的异常处理,可是这样做并非非常好.相比而言,结构化异常处理更 ...
- POJ 1252 Euro Efficiency
背包 要么 BFS 意大利是说给你几个基本的货币,组成 1~100 所有货币,使用基本上的货币量以最小的. 出口 用法概率.和最大使用量. 能够BFS 有可能 . 只是记得数组开大点. 可能会出现 1 ...
- 如何使用Maven创建web工程(详细步骤)
使用eclipse插件创建一个web project 首先创建一个Maven的Project例如以下图 我们勾选上Create a simple project (不使用骨架) 这里的Packing ...