Compartir a través de


Uso de objetos de acelerador 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 difieren en función de la cantidad de memoria, el soporte para memoria compartida, el soporte para depuración o el soporte para precisión doble. El paralelismo masivo acelerado de C++ (C++ AMP) proporciona API que puede usar para examinar los aceleradores disponibles, establecer uno como valor predeterminado, especificar varios 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 tiempo de ejecución de C++ AMP elige un acelerador predeterminado, a menos que escriba código para elegir uno específico. El tiempo de ejecución elige el acelerador predeterminado de la siguiente manera:

  1. Si la aplicación se ejecuta en modo de depuración, necesita un acelerador compatible con la depuración.

  2. De lo contrario, el acelerador especificado por la CPPAMP_DEFAULT_ACCELERATOR variable de entorno, si está configurada.

  3. De lo contrario, un dispositivo no emulado.

  4. De lo contrario, el dispositivo con la mayor cantidad de memoria disponible.

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

Además, el entorno de ejecución especifica un 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 mediante la construcción del 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 con precisión doble y la compatibilidad limitada con precisión doble 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 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 accelerator::get_all función para recuperar una lista de los aceleradores disponibles y, a continuación, 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 accelerator::get_all método para recuperar una lista de los aceleradores disponibles y, a continuación, seleccione uno en función de sus propiedades. En este ejemplo se muestra cómo seleccionar el acelerador que tiene la mayor cantidad de 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 devueltos por 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, consulte la sección "Aceleradores especiales" de este artículo.

Memoria compartida

La memoria compartida es la memoria a la que puede acceder tanto la CPU como el acelerador. El uso de memoria compartida elimina o reduce significativamente la sobrecarga de copiar datos entre la CPU y el acelerador. Aunque la memoria se comparte, tanto la CPU como el acelerador no pueden tener acceso a ella simultáneamente y, al hacerlo, 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 accelerator, por ejemplo, las matrices asociadas a accelerator, o los objetos array_view a los que se accede en accelerator.

El tiempo de ejecución 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 memoria aceleradora dedicada (no compartida) al leer desde la CPU, escribir desde la CPU o ambas. Si la memoria compartida funciona, así como memoria dedicada para leer y escribir desde la CPU, el tiempo de ejecución tiene como valor predeterminado access_type_read_write; de lo contrario, el tiempo de ejecución elige un valor predeterminado access_typemás conservador y permite que la aplicación lo invalide si los patrones de acceso a 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, a continuación, invalida su tipo de acceso predeterminado y crea un 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 siempre refleja el default_cpu_access_type con el que accelerator está asociado y no proporciona ninguna interfaz para sobrescribir ni cambiar su access_type.

Cambio del acelerador predeterminado

Puede cambiar el acelerador predeterminado llamando al accelerator::set_default método . Puede cambiar el acelerador predeterminado solo una vez por ejecución de la aplicación y debe cambiarlo antes de que se ejecute cualquier código en la GPU. Cualquier llamada de función posterior para cambiar el acelerador devuelve false. Si desea usar un acelerador diferente en una llamada a parallel_for_each, lea la sección "Usar 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 admite precisión doble.

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 accelerator_view objetos a 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 recogerá el objeto accelerator_view del array capturado en la expresión lambda.

Aceleradores especiales

Las rutas de acceso a los dispositivos de tres aceleradores especiales están disponibles como propiedades de la clase accelerator.

  • accelerator::direct3d_ref Data Member: este acelerador monohilo usa software en la CPU para emular una tarjeta gráfica genérica. Se usa de forma 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 Depurar código GPU.

  • accelerator::direct3d_warp Miembro de Datos: Este acelerador proporciona una solución de reserva para ejecutar código de C++ AMP en CPUs multinúcleo que utilizan SIMD en flujo (SSE).

  • accelerator::cpu_accelerator Miembro de datos: puede usar este acelerador para configurar matrices de almacenamiento provisional. No 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.

Interoperability

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 Class