Condividi tramite


Utilizzo degli oggetti accelerator e accelerator_view

È possibile utilizzare le classi accelerator e accelerator_view per specificare un dispositivo o un emulatore su cui eseguire il codice C++ AMP. Un sistema potrebbe avere diversi dispositivi o emulatori che differiscono per quantità di memoria, supporto della memoria condivisa, supporto di debug o supporto alla doppia precisione. Il Parallelismo Massiccio Accelerato di C++ (AMP C++) fornisce le API utilizzabili per esaminare gli acceleratori disponibili, impostarne uno come predefinito, specificare più accelerator_views per più chiamate a parallel_for_each ed eseguire attività di debug speciali.

Utilizzo degli acceleratori predefiniti

Il runtime di AMP C++ seleziona un acceleratore predefinito, ad eccezione di scrivere codice per selezionarne uno specifico. Il runtime sceglie l'acceleratore predefinito nel modo seguente:

  1. Se l'applicazione è in esecuzione in modalità di debug, un acceleratore che supporti il debug.

  2. In caso contrario, l'acceleratore specificato dalla variabile di ambiente CPPAMP_DEFAULT_ACCELERATOR, se è impostata.

  3. Altrimenti, un dispositivo non emulato.

  4. In caso contrario, il dispositivo con la maggiore quantità di memoria disponibile.

  5. Altrimenti, un dispositivo che non è incluso nella visualizzazione.

Inoltre, il runtime specifica un access_type di access_type_auto per l'acceleratore predefinito. Ciò significa che l'acceleratore predefinito utilizza la memoria condivisa se è supportata e se è noto che le caratteristiche delle prestazioni (larghezza di banda e latenza) sono uguali a quelle della memoria dedicata (non condivisa).

È possibile determinare le proprietà del acceleratore predefinito mediante la costruzione del acceleratore predefinito ed esaminandone le proprietà. Nell'esempio di codice riportato di seguito vengono visualizzati il percorso, la quantità di memoria del tasto di scelta rapida, il supporto della memoria condivisa, il supporto della precisione doppia e il supporto della precisione doppia limitato del tasto di scelta rapida predefinito.

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

Variabile di ambiente CPPAMP_DEFAULT_ACCELERATOR

È possibile impostare la variabile di ambiente CPPAMP_DEFAULT_ACCELERATOR per specificare accelerator::device_path dell'acceleratore predefinito. Il percorso è dipendente dall'hardware. Il codice seguente utilizza la funzione accelerator::get_all per recuperare un elenco di acceleratori disponibili e quindi visualizza il percorso e le caratteristiche di ogni acceleratore.

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

Selezione di un Acceleratore

Per selezionare un acceleratore, utilizzare il metodo accelerator::get_all per recuperare un elenco di acceleratori disponibili e quindi selezionarne uno in base alle relative proprietà. In questo esempio viene illustrato come selezionare l'acceleratore avente la maggior capacità di 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 degli acceleratori restituiti da accelerator::get_all è l'acceleratore della CPU.Non è possibile eseguire codice sull'acceleratore della CPU.Per filtrare l'acceleratore della CPU, confrontare il valore della proprietà device_path dell'acceleratore restituito da accelerator::get_all con il valore di accelerator::cpu_accelerator.Per ulteriori informazioni, vedere la sezione "Acceleratori Speciali" in questo articolo.

Memoria condivisa

La memoria condivisa è la memoria a cui è possibile accedere sia dalla CPU che dal tasto di scelta rapida. L'utilizzo della memoria condivisa elimina o riduce notevolmente il sovraccarico della copia di dati tra la CPU e l'acceleratore. Sebbene la memoria venga condivisa, l'accesso ad essa non può essere eseguito contemporaneamente sia dalla CPU che dal tasto di scelta rapida. L'accesso simultaneo genera un comportamento indefinito. La proprietà supports_cpu_shared_memory del tasto di scelta rapida restituisce true se il tasto di scelta rapida supporta memoria condivisa e la proprietà default_cpu_access_type ottiene l'oggetto access_type predefinito per la memoria allocata su accelerator, ad esempio, oggetti array associati a accelerator o oggetti array_view a cui si accede su accelerator.

Durante il runtime C++ AMP viene automaticamente scelto l'oggetto access_type predefinito migliore per ciascun oggetto accelerator, ma le caratteristiche di prestazione (larghezza di banda e latenza) della memoria condivisa possono essere inferiori rispetto a quelle della memoria del tasto di scelta rapida dedicata (non condivisa) durante la lettura dalla CPU, la scrittura dalla CPU o entrambi. Se il funzionamento della memoria condivisa è altrettanto appropriato di quello della memoria dedicata per la lettura e la scrittura dalla CPU, il runtime viene impostato automaticamente su access_type_read_write. In caso contrario, nel runtime viene scelto un access_type predefinito più conservativo consentendo all'applicazione di ignorarlo se i modelli di accesso alla memoria dei relativi kernel di calcolo traggono vantaggio da un access_typediverso.

Nell'esempio di codice riportato di seguito viene illustrato come determinare se il tasto di scelta rapida predefinito supporta la memoria condivisa, quindi viene eseguito l'override del tipo di accesso predefinito e da esso viene creato un oggetto 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 riflette sempre il default_cpu_access_type del accelerator a cui è associato e non fornisce alcuna interfaccia per eseguire l'override o modificarne il access_type.

Modificare gli Acceleratori Predefiniti

È possibile modificare l'acceleratore predefinito chiamando il metodo accelerator::set_default. È possibile modificare l'acceleratore predefinito solo una volta per esecuzione dell'applicazione ed è necessario modificarlo prima che il codice venga eseguito sulla GPU. Qualsiasi successiva chiamata di funzione per modificare l'acceleratore restituisce false. Se si desidera utilizzare un acceleratore diverso in una chiamata a parallel_for_each, vedere la sezione “Usare più acceleratori" in questo articolo. Nell'esempio di codice riportato viene impostato l'acceleratore predefinito ad uno che non è connesso ad un display e supporta la doppia precisione.

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

Utilizzo di più acceleratori

Esistono due modi per utilizzare più acceleratori nella propria applicazione:

  • È possibile passare oggetti accelerator_view alle chiamate del metodo parallel_for_each.

  • È possibile creare un oggetto array utilizzando un oggetto accelerator_view specifico. Il runtime di C++ AMP prenderà l'oggetto accelerator_view dall'oggetto array acquisito nella lambda espressione .

Acceleratori speciali

I percorsi di dispositivo di tre acceleratori speciali sono disponibili come proprietà della classe accelerator :

  • Membro dati accelerator::direct3d_ref: Questo acceleratore a thread singolo utilizza il software sulla CPU per emulare una scheda grafica generica. Utilizzato in maniera predefinita per il debug, ma non è utile in produzione perché è più lento degli acceleratori hardware. Inoltre, è disponibile solo in DirectX SDK e Windows SDK ed è improbabile che possa essere installato nei computer dei clienti. Per ulteriori informazioni, vedere Debug del codice GPU.

  • Membro dati accelerator::direct3d_warp: Questo acceleratore fornisce una soluzione di riserva per eseguire il codice di AMP C++ in CPU multicore che utilizzano le estensioni Streaming SIMD (SSE).

  • Membro dati accelerator::cpu_accelerator: È possibile utilizzare questo acceleratore per impostare le matrici di gestione temporanea. Non può eseguire il codice C++ AMP. Per ulteriori informazioni, vedere il post Matrici di gestione temporanea in AMP C++ sulla programmazione parallela nel blog del codice nativo.

Interoperabilità

Il runtime C++ AMP supporta l'interoperabilità tra la classe accelerator_view e l'interfaccia ID3D11Device di Direct3D. Il metodo create_accelerator_view accetta un'interfaccia IUnknown e restituisce un oggetto accelerator_view. Il metodo get_device accetta un oggetto accelerator_view e restituisce un'interfaccia IUknown.

Vedere anche

Riferimenti

Classe accelerator

Classe accelerator_view

Altre risorse

C++ AMP (C++ Accelerated Massive Parallelism)

Debug del codice GPU