windows C++ 并行编程-C++ AMP(一)

C++ AMP (C++ Accelerated Massive Parallelism) 利用数据并行硬件(通常作为独立显卡上的图形处理单元 (GPU) 存在)来加速 C++ 代码的执行。 C++ AMP 编程模型包括多维数组、索引、内存传输和平铺的支持。 它还包括数学函数库。 可以使用 C++ AMP 语言扩展来控制如何在 CPU 与 GPU 之间来回移动数据。 

从 Visual Studio 2022 版本 17.0 开始,已弃用 C++ AMP 头文件。 包含任何 AMP 头文件都会导致生成错误。 应在包含任何 AMP 头文件之前定义 _SILENCE_AMP_DEPRECATION_WARNINGS,以使警告静音。

使用平铺

可以使用平铺来最大化应用的加速。 平铺将线程分成相等的矩形子集或平铺。 如果使用适当的平铺大小和平铺算法,则可以从 C++ AMP 代码中实现更好的加速。 平铺的基本组件为:

  • tile_static 变量。 平铺的主要好处是可以从 tile_static 访问中获得性能增益。 访问 tile_static 内存中的数据可能比访问全局空间(array 或 array_view 对象)中的数据要快得多。 为每个平铺创建 tile_static 变量的实例,平铺中的所有线程可以访问该变量。 在典型的平铺算法中,数据从全局内存复制到 tile_static 内存一次,然后从 tile_static 内存被访问多次;
  • tile_barrier::wait 方法。 tile_barrier::wait 调用会暂停当前线程的执行,直到同一平铺中的所有线程都到达 tile_barrier::wait 调用。 不能保证线程的运行顺序,只有在所有线程都到达 tile_barrier::wait 调用之后,平铺中的线程才越过该调用执行。 这意味着,使用 tile_barrier::wait 方法可以逐平铺而不是逐线程执行任务。 典型的平铺算法提供用于初始化整个平铺的 tile_static 内存,然后调用 tile_barrier::wait 的代码。 tile_barrier::wait 后面的代码包含需要访问所有 tile_static 值的计算;
  • 局部和全局索引。 可以访问相对于整个 array_view 或 array 对象的索引以及相对于平铺的索引。 使用局部索引可使代码更易于阅读和调试。 通常,你会使用局部索引来访问 tile_static 变量,并使用全局索引来访问 array 和 array_view 变量;
  • tiled_extent 类和 tiled_index 类。 在 parallel_for_each 调用中使用 tiled_extent 对象而不是 extent 对象。 在 parallel_for_each 调用中使用 tiled_index 对象而不是 index 对象;

若要利用平铺,算法必须将计算域分区成平铺,然后将平铺数据复制到 tile_static 变量中以加快访问速度。

全局、平铺和局部索引的示例

下图显示了排列在 2x3 平铺中的 8x9 数据矩阵。

以下示例显示了此平铺矩阵的全局、平铺和局部索引。 array_view 对象是使用 Description 类型的元素创建的。 Description 保存矩阵中元素的全局、平铺和局部索引。 parallel_for_each 调用中的代码设置每个元素的全局、平铺和局部索引值。 输出显示 Description 结构中的值。

#include <iostream>
#include <iomanip>
#include <Windows.h>
#include <amp.h>
using namespace concurrency;

const int ROWS = 8;
const int COLS = 9;

// tileRow and tileColumn specify the tile that each thread is in.
// globalRow and globalColumn specify the location of the thread in the array_view.
// localRow and localColumn specify the location of the thread relative to the tile.
struct Description {
    int value;
    int tileRow;
    int tileColumn;
    int globalRow;
    int globalColumn;
    int localRow;
    int localColumn;
};

// A helper function for formatting the output.
void SetConsoleColor(int color) {
    int colorValue = (color == 0)  4 : 2;
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colorValue);
}

// A helper function for formatting the output.
void SetConsoleSize(int height, int width) {
    COORD coord;

    coord.X = width;
    coord.Y = height;
    SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coord);

    SMALL_RECT* rect = new SMALL_RECT();
    rect->Left = 0;
    rect->Top = 0;
    rect->Right = width;
    rect->Bottom = height;
    SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), true, rect);
}

// This method creates an 8x9 matrix of Description structures.
// In the call to parallel_for_each, the structure is updated
// with tile, global, and local indices.
void TilingDescription() {
    // Create 72 (8x9) Description structures.
    std::vector<Description> descs;
    for (int i = 0; i < ROWS * COLS; i++) {
        Description d = {i, 0, 0, 0, 0, 0, 0};
        descs.push_back(d);
    }

    // Create an array_view from the Description structures.
    extent<2> matrix(ROWS, COLS);
    array_view<Description, 2> descriptions(matrix, descs);

    // Update each Description with the tile, global, and local indices.
    parallel_for_each(descriptions.extent.tile< 2, 3>(),
        [=] (tiled_index< 2, 3> t_idx) restrict(amp)
    {
        descriptions[t_idx].globalRow = t_idx.global[0];
        descriptions[t_idx].globalColumn = t_idx.global[1];
        descriptions[t_idx].tileRow = t_idx.tile[0];
        descriptions[t_idx].tileColumn = t_idx.tile[1];
        descriptions[t_idx].localRow = t_idx.local[0];
        descriptions[t_idx].localColumn= t_idx.local[1];
    });

    // Print out the Description structure for each element in the matrix.
    // Tiles are displayed in red and green to distinguish them from each other.
    SetConsoleSize(100, 150);
    for (int row = 0; row < ROWS; row++) {
        for (int column = 0; column < COLS; column++) {
            SetConsoleColor((descriptions(row, column).tileRow + descriptions(row, column).tileColumn) % 2);
            std::cout << "Value: " << std::setw(2) << descriptions(row, column).value << "      ";
        }
        std::cout << "\n";

        for (int column = 0; column < COLS; column++) {
            SetConsoleColor((descriptions(row, column).tileRow + descriptions(row, column).tileColumn) % 2);
            std::cout << "Tile:   " << "(" << descriptions(row, column).tileRow << "," << descriptions(row, column).tileColumn << ")  ";
        }
        std::cout << "\n";

        for (int column = 0; column < COLS; column++) {
            SetConsoleColor((descriptions(row, column).tileRow + descriptions(row, column).tileColumn) % 2);
            std::cout << "Global: " << "(" << descriptions(row, column).globalRow << "," << descriptions(row, column).globalColumn << ")  ";
        }
        std::cout << "\n";

        for (int column = 0; column < COLS; column++) {
            SetConsoleColor((descriptions(row, column).tileRow + descriptions(row, column).tileColumn) % 2);
            std::cout << "Local:  " << "(" << descriptions(row, column).localRow << "," << descriptions(row, column).localColumn << ")  ";
        }
        std::cout << "\n";
        std::cout << "\n";
    }
}

int main() {
    TilingDescription();
    char wait;
    std::cin >> wait;
}

该示例的主要工作是定义 array_view 对象和 parallel_for_each 调用。

  • Description 结构的向量复制到 8x9 array_view 对象中;
  • 使用 tiled_extent 对象作为计算域来调用 parallel_for_each 方法。 tiled_extent 对象是通过调用 descriptions 变量的 extent::tile() 方法创建的。 extent::tile() 调用的类型参数 <2,3> 指定创建 2x3 平铺。 因此,8x9 矩阵平铺成 12 个平铺图块(4 行 x 3列);
  • 使用 tiled_index<2,3> 对象 (t_idx) 作为索引来调用 parallel_for_each 方法。 索引 (t_idx) 的类型参数必须与计算域 (descriptions.extent.tile< 2, 3>()) 的类型参数匹配;
  • 执行每个线程时,索引 t_idx 会返回有关线程所在平铺(tiled_index::tile 属性)以及线程在平铺中的位置(tiled_index::local 属性)的信息;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值