Использование объектов accelerator и accelerator_view

С помощью акселератора и accelerator_view классов можно указать устройство или эмулятор для запуска кода C++ AMP. Система может иметь несколько устройств или эмуляторов, которые отличаются объемом памяти, поддержкой общей памяти, поддержкой отладки или поддержкой двойной точности. C++ Accelerated Massive Parallelism (C++ AMP) предоставляет API, которые можно использовать для изучения доступных акселераторов, задания одного в качестве значения по умолчанию, указания нескольких accelerator_views для нескольких вызовов для parallel_for_each и выполнения специальных задач отладки.

Примечание.

Заголовки C++ AMP устарели начиная с Visual Studio 2022 версии 17.0. Включение всех заголовков AMP приведет к возникновению ошибок сборки. Определите _SILENCE_AMP_DEPRECATION_WARNINGS перед включением всех заголовков AMP, чтобы замолчать предупреждения.

Использование акселератора по умолчанию

Среда выполнения C++ AMP выбирает акселератор по умолчанию, если только код не будет выбран для конкретного. Среда выполнения выбирает акселератор по умолчанию следующим образом:

  1. Если приложение работает в режиме отладки, акселератор, поддерживающий отладку.

  2. В противном случае ускоритель, заданный переменной CPPAMP_DEFAULT_ACCELERATOR среды, если он задан.

  3. В противном случае неэмулированное устройство.

  4. В противном случае устройство с наибольшим объемом доступной памяти.

  5. В противном случае устройство, которое не подключено к экрану.

Кроме того, среда выполнения указывает для 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 ЦП, является ускоритель ЦП. Невозможно выполнить код на акселераторе ЦП. Чтобы отфильтровать акселератор ЦП, сравните значение свойства device_path акселератора, возвращаемого accelerator::get_all со значением акселератора::cpu_accelerator. Дополнительные сведения см. в разделе "Специальные акселераторы" в этой статье.

Общая память

Общая память — это память, доступ к которой осуществляется как ЦП, так и акселератором. Использование общей памяти устраняет или значительно сокращает затраты на копирование данных между ЦП и акселератором. Хотя память предоставляется совместно, доступ к ней не может одновременно выполняться как ЦП, так и акселератором, и это приводит к неопределенному поведению. Свойство акселератора supports_cpu_shared_memory возвращаетсяtrue, если акселератор поддерживает общую память, а свойство default_cpu_access_type получает access_type по умолчанию для памяти, выделенной для acceleratorпамяти, например массивов, связанных с acceleratorобъектом или array_view объектами, к которым обращается доступaccelerator.

Среда выполнения C++ AMP автоматически выбирает оптимальное значение по умолчанию access_type для каждого accelerator, но характеристики производительности (пропускная способность и задержка) общей памяти могут быть хуже, чем выделенные (не общие) ускорители памяти при чтении из ЦП, записи из ЦП или обоих. Если общая память выполняется, а также выделенная память для чтения и записи из ЦП, среда выполнения по умолчанию имеет значение; в противном случае среда выполнения выбирает более консервативный параметр по умолчанию 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 отражает default_cpu_access_typeaccelerator связанный с ним интерфейс, и он не предоставляет интерфейс для переопределения или изменения его 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 будет собирать accelerator_view объект из захваченного объекта массива в лямбда-выражении.

Специальные акселераторы

Пути к устройству трех специальных акселераторов доступны в качестве свойств accelerator класса:

  • accelerator::d irect3d_ref Data Member: этот однопоточный акселератор использует программное обеспечение на ЦП для эмуляции универсальной графической карта. Он используется по умолчанию для отладки, но он не полезен в рабочей среде, так как он медленнее, чем аппаратные акселераторы. Кроме того, он доступен только в пакете SDK DirectX и пакете SDK для Windows, и вряд ли он будет установлен на компьютерах клиентов. Дополнительные сведения см. в разделе Отладка кода GPU.

  • accelerator::d irect3d_warp Data Member: этот акселератор предоставляет резервное решение для выполнения кода C++ AMP на многоядерных ЦП, использующих расширения ПОТОКОВой SIMD (SSE).

  • акселератор::cpu_accelerator элемент данных: этот акселератор можно использовать для настройки промежуточных массивов. Не удается выполнить код C++ AMP. Дополнительные сведения см . в записи staging Arrays in C++ AMP в блоге о параллельном программировании в машинном коде.

Совместимость

Среда выполнения C++ AMP поддерживает взаимодействие между accelerator_view классом и интерфейсом Direct3D ID3D11Device. Метод create_accelerator_view принимает IUnknown интерфейс и возвращает accelerator_view объект. Метод get_device принимает accelerator_view объект и возвращает IUnknown интерфейс.

См. также

C++ AMP (C++ Accelerated Massive Parallelism)
Отладка кода GPU
Класс accelerator_view