可以使用 加速器 和 accelerator_view 类指定要运行 C++ AMP 代码的设备或模拟器。 系统可能有多个设备或模拟器,这些设备或仿真器因内存量、共享内存支持、调试支持或双精度支持而异。 C++ 加速大规模并行(C++ AMP)提供了一些 API,您可以使用它们检查可用的加速器,将其中一个设置为默认加速器,为多次调用 parallel_for_each 指定多个 accelerator_views,以及执行特殊的调试任务。
注释
从 Visual Studio 2022 版本 17.0 开始,已弃用 C++ AMP 标头。
包含任何 AMP 标头都会引发构建错误。 应在包含任何 AMP 标头之前定义 _SILENCE_AMP_DEPRECATION_WARNINGS,以使警告静音。
使用默认加速器
除非编写代码来选取特定加速器,否则 C++ AMP 运行时会选取默认加速器。 运行时选择默认加速器,如下所示:
如果应用程序在调试模式下运行,则需要一个支持调试功能的加速器。
否则,使用由环境变量
CPPAMP_DEFAULT_ACCELERATOR指定的加速器(如果已设置)。否则为非模拟设备。
否则,具有最大可用内存量的设备。
否则,未连接到显示器的设备。
此外,运行时为默认加速器指定了access_typeaccess_type_auto。 这意味着,如果支持共享内存,并且默认加速器的性能特征(带宽和延迟)已知与专用(非共享)内存相同,则默认加速器使用共享内存。
可以通过构造默认加速器并检查其属性来确定默认加速器的属性。 下面的代码示例打印路径、加速器内存量、共享内存支持、双精度支持以及默认加速器的有限双精度支持。
void default_properties() {
accelerator default_acc;
std::wcout << default_acc.device_path << "\n";
std::wcout << default_acc.dedicated_memory << "\n";
std::wcout << (accs[i].supports_cpu_shared_memory ?
"CPU shared memory: true" : "CPU shared memory: false") << "\n";
std::wcout << (accs[i].supports_double_precision ?
"double precision: true" : "double precision: false") << "\n";
std::wcout << (accs[i].supports_limited_double_precision ?
"limited double precision: true" : "limited double precision: false") << "\n";
}
CPPAMP_DEFAULT_ACCELERATOR环境变量
可以设置 CPPAMP_DEFAULT_ACCELERATOR 环境变量以指定 accelerator::device_path 默认加速器。 路径依赖于硬件。 以下代码使用 accelerator::get_all 函数检索可用加速器的列表,然后显示每个加速器的路径和特征。
void list_all_accelerators()
{
std::vector<accelerator> accs = accelerator::get_all();
for (int i = 0; i <accs.size(); i++) {
std::wcout << accs[i].device_path << "\n";
std::wcout << accs[i].dedicated_memory << "\n";
std::wcout << (accs[i].supports_cpu_shared_memory ?
"CPU shared memory: true" : "CPU shared memory: false") << "\n";
std::wcout << (accs[i].supports_double_precision ?
"double precision: true" : "double precision: false") << "\n";
std::wcout << (accs[i].supports_limited_double_precision ?
"limited double precision: true" : "limited double precision: false") << "\n";
}
}
选择加速器
若要选择加速器,请使用 accelerator::get_all 该方法检索可用加速器的列表,然后根据其属性选择一个。 此示例演示如何选取具有最大内存的加速器:
void pick_with_most_memory()
{
std::vector<accelerator> accs = accelerator::get_all();
accelerator acc_chosen = accs[0];
for (int i = 0; i <accs.size(); i++) {
if (accs[i].dedicated_memory> acc_chosen.dedicated_memory) {
acc_chosen = accs[i];
}
}
std::wcout << "The accelerator with the most memory is "
<< acc_chosen.device_path << "\n"
<< acc_chosen.dedicated_memory << ".\n";
}
注释
返回accelerator::get_all 的加速器之一是 CPU 加速器。 不能在 CPU 加速器上执行代码。 若要筛选掉 CPU 加速器,请将返回的加速器的 accelerator::get_all 属性的值与快捷键::cpu_accelerator的值进行比较。 有关详细信息,请参阅本文中的“特殊加速器”部分。
共享内存
共享内存是 CPU 和加速器可以访问的内存。 使用共享内存可以消除或显著减少在 CPU 和加速器之间复制数据的开销。 尽管内存是共享的,但 CPU 和加速器不能同时访问内存,这样做会导致未定义的行为。 如果加速器属性supports_cpu_shared_memory支持共享内存,则返回true,并且default_cpu_access_type属性获取在accelerator上分配的内存的默认access_type,例如,与accelerator相关联的数组,或在accelerator上访问的array_view。
C++ AMP 运行时自动为每个access_type内存选择最佳默认值accelerator,但共享内存的性能特征(带宽和延迟)可能比从 CPU 读取、从 CPU 写入或同时读取专用(非共享)加速器内存的性能特征(带宽和延迟)更差。 如果共享内存执行得与专用内存一样好,从 CPU 进行读取和写入时,运行时会默认使用 access_type_read_write;否则,运行时选择更保守的默认值 access_type,并允许应用程序在计算内核的内存访问模式从不同的 access_type 中获益时对其进行覆盖。
下面的代码示例演示如何确定默认加速器是否支持共享内存,并覆盖其默认访问类型,然后从中创建一个accelerator_view。
#include <amp.h>
#include <iostream>
using namespace Concurrency;
int main()
{
accelerator acc = accelerator(accelerator::default_accelerator);
// Early out if the default accelerator doesn't support shared memory.
if (!acc.supports_cpu_shared_memory)
{
std::cout << "The default accelerator does not support shared memory" << std::endl;
return 1;
}
// Override the default CPU access type.
acc.set_default_cpu_access_type(access_type_read_write);
// Create an accelerator_view from the default accelerator. The
// accelerator_view reflects the default_cpu_access_type of the
// accelerator it's associated with.
accelerator_view acc_v = acc.default_view;
}
始终accelerator_view反映与accelerator关联的default_cpu_access_type,并且不提供替代或更改其access_type的方法。
更改默认加速器
可以通过调用 accelerator::set_default 该方法来更改默认加速器。 每个应用执行只能更改默认加速器一次,并且必须在 GPU 上执行任何代码之前对其进行更改。 任何后续的函数调用以更改加速器都会返回false。 如果要在调用 parallel_for_each中使用其他加速器,请阅读本文中的“使用多个加速器”部分。 下面的代码示例将默认加速器设置为未模拟、未连接到显示器且支持双精度的加速器。
bool pick_accelerator()
{
std::vector<accelerator> accs = accelerator::get_all();
accelerator chosen_one;
auto result = std::find_if(accs.begin(), accs.end(),
[] (const accelerator& acc) {
return !acc.is_emulated &&
acc.supports_double_precision &&
!acc.has_display;
});
if (result != accs.end()) {
chosen_one = *(result);
}
std::wcout <<chosen_one.description <<std::endl;
bool success = accelerator::set_default(chosen_one.device_path);
return success;
}
使用多个加速器
在应用中使用多个加速器有两种方法:
可以将
accelerator_view对象传递给对 parallel_for_each 方法的调用。可以使用特定对象构造
accelerator_view对象。 C++ AMP 运行时将从 lambda 表达式中捕获的accelerator_view对象中选取该对象。
特殊加速器
特殊加速器的三个设备路径作为accelerator类的属性是可用的:
accelerator::direct3d_ref 数据成员:此单线程加速器使用 CPU 上的软件来仿真通用图形卡。 它默认用于调试,但它在生产中并不有用,因为它比硬件加速器慢。 此外,它仅在 DirectX SDK 和 Windows SDK 中可用,并且不太可能安装在客户的计算机上。 有关详细信息,请参阅 调试 GPU 代码。
accelerator::direct3d_warp 数据成员:此加速器提供用于在使用流式 SIMD 扩展(SSE)的多核 CPU 上执行 C++ AMP 代码的后备解决方案。
accelerator::cpu_accelerator 数据成员:可以使用此加速器来设置暂存数组。 它无法执行 C++ AMP 代码。 有关详细信息,请参阅本机代码中并行编程博客上 C++ AMP 中的暂存数组 文章。
Interoperability
C++ AMP 运行时支持类与 accelerator_view之间的互操作性。
create_accelerator_view方法采用接口IUnknown并返回对象accelerator_view。
get_device方法采用对象accelerator_view并返回IUnknown接口。