Compartir a través de


Usar objetos accelerator y accelerator_view

Puede usar el acelerador y accelerator_view clases para especificar el dispositivo o emulador en el que ejecutar el código de AMP de C++. Un sistema puede tener varios dispositivos o emuladores que difieran según la cantidad de memoria, la compatibilidad con la memoria compartida, la compatibilidad con la depuración o la compatibilidad de doble precisión. C++ Accelerated Massive Parallelism (C++ AMP) proporciona API que puede usar para examinar los aceleradores disponibles, establecer uno como predeterminado, especificar varios objetos accelerator_views para varias llamadas a parallel_for_each y realizar tareas de depuración especiales.

Nota:

Los encabezados de C++ AMP están en desuso a partir de la versión 17.0 de Visual Studio 2022. Si se incluyen encabezados AMP, se generarán errores de compilación. Defina _SILENCE_AMP_DEPRECATION_WARNINGS antes de incluir encabezados AMP para silenciar las advertencias.

Uso del acelerador predeterminado

El runtime de C++ AMP elige un acelerador predeterminado, a menos que escriba código para elegir uno específico. El runtime elige el acelerador predeterminado de la siguiente manera:

  1. Si la aplicación se ejecuta en modo de depuración, elegirá un acelerador que admita la depuración.

  2. De lo contrario, el acelerador especificado por la CPPAMP_DEFAULT_ACCELERATOR variable de entorno, si se establece.

  3. De lo contrario, elegirá un dispositivo no emulado.

  4. De lo contrario, elegirá el dispositivo que tenga la mayor cantidad de memoria disponible.

  5. De lo contrario, elegirá un dispositivo que no esté conectado a la pantalla.

Además, el runtime especifica un elemento access_type de access_type_auto para el acelerador predeterminado. Esto significa que el acelerador predeterminado usa memoria compartida si se admite y si se sabe que sus características de rendimiento (ancho de banda y latencia) son iguales que la memoria dedicada (no compartida).

Puede determinar las propiedades del acelerador predeterminado construyendo el acelerador predeterminado y el examen de sus propiedades. En el ejemplo de código siguiente se imprime la ruta de acceso, la cantidad de memoria del acelerador, la compatibilidad con memoria compartida, la compatibilidad de doble precisión y la compatibilidad limitada de doble precisión del acelerador predeterminado.

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";
}

Variable de entorno de CPPAMP_DEFAULT_ACCELERATOR

Puede establecer la CPPAMP_DEFAULT_ACCELERATOR variable de entorno para especificar el accelerator::device_path del acelerador predeterminado. La ruta de acceso depende del hardware. El código siguiente usa la función accelerator::get_all para recuperar una lista de los aceleradores disponibles y, después, muestra la ruta de acceso y las características de cada acelerador.

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";
    }
}

Selección de un acelerador

Para seleccionar un acelerador, use el método accelerator::get_all a fin de recuperar una lista de los aceleradores disponibles y, después, seleccione uno en función de sus propiedades. En este ejemplo se muestra cómo seleccionar el acelerador que tiene más memoria:

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";
}

Nota:

Uno de los aceleradores que devuelve accelerator::get_all es el acelerador de CPU. No se puede ejecutar código en el acelerador de CPU. Para filtrar el acelerador de CPU, compare el valor de la propiedad device_path del acelerador devuelto por accelerator::get_all con el valor del acelerador::cpu_accelerator. Para obtener más información, vea la sección "Aceleradores especiales" en este artículo.

Memoria compartida

La memoria compartida es aquella a la que pueden acceder tanto la CPU como el acelerador. El uso de memoria compartida elimina o reduce considerablemente la sobrecarga de copiar datos entre la CPU y el acelerador. Aunque la memoria se comparte, la CPU y el acelerador no pueden acceder a ella simultáneamente; si lo hacen, se produce un comportamiento indefinido. La propiedad del acelerador supports_cpu_shared_memory devuelve true si el acelerador admite memoria compartida y la propiedad default_cpu_access_type obtiene el access_type predeterminado para la memoria asignada en el accelerator, por ejemplo, para las matrices asociadas con el accelerator, o para los objetos array_view a los que se accede en el accelerator.

El runtime de C++ AMP elige automáticamente el mejor valor predeterminado access_type para cada accelerator, pero las características de rendimiento (ancho de banda y latencia) de la memoria compartida pueden ser peores que las de la memoria del acelerador dedicada (no compartida) al leer desde la CPU, escribir desde la CPU o en ambos casos. Si la memoria compartida funciona tan bien como la memoria dedicada para leer y escribir desde la CPU, el runtime tiene como valor predeterminado access_type_read_write; de lo contrario, el runtime elige un valor predeterminado access_type más conservador y permite a la aplicación invalidarlo si los patrones de acceso a la memoria de sus kernels de cálculo se benefician de otro access_type.

En el ejemplo de código siguiente se muestra cómo determinar si el acelerador predeterminado admite memoria compartida y, después, invalida su tipo de acceso predeterminado y crea un elemento accelerator_view a partir de él.

#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;
}

Un accelerator_view objeto siempre refleja el default_cpu_access_type de que accelerator está asociado y no proporciona ninguna interfaz para invalidar ni cambiar su access_type.

Cambio del acelerador predeterminado

Puede cambiar el acelerador predeterminado llamando al método accelerator::set_default. Puede cambiar el acelerador predeterminado solo una vez por cada ejecución de la aplicación y debe cambiarlo antes de que se ejecute cualquier código en la GPU. Las llamadas de función posteriores para cambiar el acelerador devuelven false. Si quiere usar un acelerador diferente en una llamada a parallel_for_each, lea la sección "Uso de varios aceleradores" de este artículo. En el ejemplo de código siguiente se establece el acelerador predeterminado en uno que no se emula, no está conectado a una pantalla y es compatible con la doble precisión.

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;
}

Uso de varios aceleradores

Hay dos maneras de usar varios aceleradores en la aplicación:

  • Puede pasar objetos accelerator_view en las llamadas al método parallel_for_each.

  • Puede construir un objeto de matriz mediante un objeto específico accelerator_view . El tiempo de ejecución de C++ AMP recuperará el objeto accelerator_view a partir del objeto arreglo capturado en la expresión lambda.

Aceleradores especiales

Las rutas de acceso de dispositivo de tres aceleradores especiales están disponibles como propiedades de la clase accelerator:

  • accelerator::direct3d_ref Data Member: Este acelerador monohilo utiliza software en la CPU para emular una tarjeta gráfica genérica. Se usa de manera predeterminada para la depuración, pero no es útil en producción porque es más lento que los aceleradores de hardware. Además, solo está disponible en el SDK de DirectX y Windows SDK, y es poco probable que se instale en los equipos de los clientes. Para obtener más información, consulte Depuración de código de GPU.

  • accelerator::direct3d_warp Data Member: Este acelerador proporciona una solución de reserva para ejecutar código de C++ AMP en CPU de varios núcleos que usan extensiones SIMD de streaming (SSE).

  • accelerator::cpu_accelerator Miembro de datos: puede usar este acelerador para configurar matrices de almacenamiento provisional. No se puede ejecutar código de C++ AMP. Para obtener más información, vea la entrada Matrices de ensayo en C++ AMP en el blog Programación paralela en código nativo.

Interoperabilidad

El entorno de ejecución de C++ AMP admite la interoperabilidad entre la accelerator_view clase y la interfaz Direct3D ID3D11Device. El método create_accelerator_view toma una IUnknown interfaz y devuelve un accelerator_view objeto . El método get_device toma un accelerator_view objeto y devuelve una IUnknown interfaz.

Consulte también

C++ AMP (Paralelismo masivo acelerado de C++)
Depuración de código de GPU
accelerator_view (clase)