Поделиться через


Использование акселератора и объектов 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_type как access_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::direct3d_ref Data Member: этот однопоточный акселератор использует программу на центральном процессоре для эмуляции универсального графического адаптера. Он используется по умолчанию для отладки, но он не полезен в рабочей среде, так как он медленнее, чем аппаратные акселераторы. Кроме того, он доступен только в пакете SDK DirectX и пакете SDK для Windows, и вряд ли он будет установлен на компьютерах клиентов. Дополнительные сведения см. в разделе Отладка кода GPU.

  • accelerator::direct3d_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++ Ускоренная массовая параллелизм)
Отладка кода GPU
Класс accelerator_view