Utilisation des objets accelerator et accelerator_view

Vous pouvez utiliser l’accélérateur et les classes accelerator_view pour spécifier l’appareil ou l’émulateur sur lequel exécuter votre code AMP C++. Un système peut avoir plusieurs appareils ou émulateurs qui diffèrent par quantité de mémoire, prise en charge de la mémoire partagée, prise en charge du débogage ou prise en charge double précision. C++ Accelerated Massive Parallelism (C++ AMP) fournit des API que vous pouvez utiliser pour examiner les accélérateurs disponibles, définir l’un comme valeur par défaut, spécifier plusieurs accelerator_views pour plusieurs appels à parallel_for_each et effectuer des tâches de débogage spéciales.

Remarque

Les en-têtes AMP C++ sont déconseillés à partir de Visual Studio 2022 version 17.0. L’inclusion d’en-têtes AMP génère des erreurs de génération. Définissez _SILENCE_AMP_DEPRECATION_WARNINGS avant d’inclure tous les en-têtes AMP pour silence les avertissements.

Utilisation de l’accélérateur par défaut

Le runtime AMP C++ sélectionne un accélérateur par défaut, sauf si vous écrivez du code pour en choisir un spécifique. Le runtime choisit l’accélérateur par défaut comme suit :

  1. Si l’application s’exécute en mode débogage, un accélérateur prenant en charge le débogage.

  2. Sinon, l’accélérateur spécifié par la CPPAMP_DEFAULT_ACCELERATOR variable d’environnement, s’il est défini.

  3. Sinon, un appareil non émulé.

  4. Sinon, l’appareil qui a la plus grande quantité de mémoire disponible.

  5. Sinon, un appareil qui n’est pas attaché à l’affichage.

En outre, le runtime spécifie un access_type élément de access_type_auto l’accélérateur par défaut. Cela signifie que l’accélérateur par défaut utilise la mémoire partagée si elle est prise en charge et si ses caractéristiques de performances (bande passante et latence) sont connues comme étant identiques à la mémoire dédiée (non partagée).

Vous pouvez déterminer les propriétés de l’accélérateur par défaut en construisant l’accélérateur par défaut et en examinant ses propriétés. L’exemple de code suivant imprime le chemin, la quantité de mémoire de l’accélérateur, la prise en charge de la mémoire partagée, la prise en charge double précision et la prise en charge limitée de l’accélérateur par défaut.

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 d’environnement CPPAMP_DEFAULT_ACCELERATOR

Vous pouvez définir la variable d’environnement CPPAMP_DEFAULT_ACCELERATOR pour spécifier l’accélérateur accelerator::device_path par défaut. Le chemin d’accès dépend du matériel. Le code suivant utilise la accelerator::get_all fonction pour récupérer une liste des accélérateurs disponibles, puis affiche le chemin et les caractéristiques de chaque accélérateur.

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

Sélection d’un accélérateur

Pour sélectionner un accélérateur, utilisez la accelerator::get_all méthode pour récupérer une liste des accélérateurs disponibles, puis sélectionnez-en une en fonction de ses propriétés. Cet exemple montre comment choisir l’accélérateur qui a le plus de mémoire :

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

Remarque

L’un des accélérateurs retournés par accelerator::get_all est l’accélérateur processeur. Vous ne pouvez pas exécuter de code sur l’accélérateur d’UC. Pour filtrer l’accélérateur d’UC, comparez la valeur de la propriété device_path de l’accélérateur retourné par accelerator::get_all la valeur de l’accélérateur ::cpu_accelerator. Pour plus d’informations, consultez la section « Accélérateurs spéciaux » de cet article.

Mémoire partagée

La mémoire partagée est la mémoire accessible à la fois par le processeur et l’accélérateur. L’utilisation de la mémoire partagée élimine ou réduit considérablement la surcharge liée à la copie de données entre l’UC et l’accélérateur. Bien que la mémoire soit partagée, elle ne peut pas être accessible simultanément par l’UC et l’accélérateur, et cela provoque un comportement non défini. La propriété accélérateur supports_cpu_shared_memory retourne true si l’accélérateur prend en charge la mémoire partagée et que la propriété default_cpu_access_type obtient la access_type par défaut pour la mémoire allouée sur le acceleratortableauassocié au acceleratortableau ou array_view aux objets accessibles sur le accelerator.

Le runtime AMP C++ choisit automatiquement la meilleure valeur par défaut access_type pour chacun accelerator, mais les caractéristiques de performances (bande passante et latence) de la mémoire partagée peuvent être pires que celles de la mémoire accélérateur dédiée (non partagée) lors de la lecture à partir de l’UC, l’écriture à partir de l’UC, ou les deux. Si la mémoire partagée fonctionne ainsi que la mémoire dédiée pour la lecture et l’écriture à partir de l’UC, le runtime a la valeur par défaut access_type_read_write; sinon, le runtime choisit une valeur par défaut access_typeplus conservatrice et permet à l’application de la remplacer si les modèles d’accès à la mémoire de ses noyaux de calcul bénéficient d’un autre access_type.

L’exemple de code suivant montre comment déterminer si l’accélérateur par défaut prend en charge la mémoire partagée, puis remplace son type d’accès par défaut et en crée un 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;
}

Un accelerator_view élément reflète toujours l’élément default_cpu_access_typeaccelerator avec qui il est associé, et il ne fournit aucune interface pour remplacer ou modifier son access_type.

Modification de l’accélérateur par défaut

Vous pouvez modifier l’accélérateur par défaut en appelant la accelerator::set_default méthode. Vous ne pouvez modifier l’accélérateur par défaut qu’une seule fois par exécution de l’application et vous devez le modifier avant l’exécution d’un code sur le GPU. Tous les appels de fonction suivants pour modifier le retour falsede l’accélérateur . Si vous souhaitez utiliser un autre accélérateur dans un appel, parallel_for_eachlisez la section « Utilisation de plusieurs accélérateurs » dans cet article. L’exemple de code suivant définit l’accélérateur par défaut sur un accélérateur qui n’est pas émulé, n’est pas connecté à un affichage et prend en charge la double précision.

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

Utilisation de plusieurs accélérateurs

Il existe deux façons d’utiliser plusieurs accélérateurs dans votre application :

  • Vous pouvez transmettre des accelerator_view objets aux appels à la méthode parallel_for_each .

  • Vous pouvez construire un objet tableau à l’aide d’un objet spécifique accelerator_view . Le runtime C+AMP récupère l’objet accelerator_view à partir de l’objet tableau capturé dans l’expression lambda.

Accélérateurs spéciaux

Les chemins d’accès des appareils de trois accélérateurs spéciaux sont disponibles en tant que propriétés de la accelerator classe :

  • accelerator ::d irect3d_ref Data Member : cet accélérateur à thread unique utilise des logiciels sur l’UC pour émuler un carte graphique générique. Il est utilisé par défaut pour le débogage, mais il n’est pas utile en production, car il est plus lent que les accélérateurs matériels. En outre, il est disponible uniquement dans le Kit de développement logiciel (SDK) DirectX et le Kit de développement logiciel (SDK) Windows, et il est peu probable qu’il soit installé sur les ordinateurs de vos clients. Pour plus d’informations, consultez Débogage du code GPU.

  • accelerator ::d irect3d_warp Data Member : cet accélérateur fournit une solution de secours pour l’exécution du code AMP C++ sur des processeurs multicœurs qui utilisent les extensions SIMD de streaming (SSE).

  • accelerator ::cpu_accelerator Membre de données : vous pouvez utiliser cet accélérateur pour configurer des tableaux intermédiaires. Il ne peut pas exécuter de code AMP C++. Pour plus d’informations, consultez le billet AMP C++ sur le blog de programmation parallèle dans le code natif.

Interopérabilité

Le runtime AMP C++ prend en charge l’interopérabilité entre la accelerator_view classe et l’interface Direct3D ID3D11Device. La méthode create_accelerator_view prend une IUnknown interface et retourne un accelerator_view objet. La méthode get_device prend un accelerator_view objet et retourne une IUnknown interface.

Voir aussi

C++ AMP (C++ Accelerated Massive Parallelism)
Débogage du code GPU
accelerator_view, classe