Utilizzo degli oggetti accelerator e accelerator_view
È possibile usare l'acceleratore e le classi accelerator_view per specificare il dispositivo o l'emulatore in cui eseguire il codice AMP C++. Un sistema potrebbe avere diversi dispositivi o emulatori che differiscono in base alla quantità di memoria, al supporto della memoria condivisa, al supporto del debug o al supporto a precisione doppia. C++ Accelerated Massive Parallelism (C++ AMP) fornisce API che è possibile usare 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.
Nota
Le intestazioni C++ AMP sono deprecate a partire da Visual Studio 2022 versione 17.0.
L'inclusione di eventuali intestazioni AMP genererà errori di compilazione. Definire _SILENCE_AMP_DEPRECATION_WARNINGS
prima di includere eventuali intestazioni AMP per disattivare gli avvisi.
Uso dell'acceleratore predefinito
Il runtime C++ AMP seleziona un acceleratore predefinito, a meno che non si scriva codice per sceglierne uno specifico. Il runtime sceglie l'acceleratore predefinito come segue:
Se l'app è in esecuzione in modalità di debug, un acceleratore che supporta il debug.
In caso contrario, l'acceleratore specificato dalla
CPPAMP_DEFAULT_ACCELERATOR
variabile di ambiente, se impostato.In caso contrario, un dispositivo non emulato.
In caso contrario, il dispositivo con la massima quantità di memoria disponibile.
In caso contrario, un dispositivo non collegato allo schermo.
Inoltre, il runtime specifica un access_type
di access_type_auto
per l'acceleratore predefinito. Ciò significa che l'acceleratore predefinito usa la memoria condivisa se è supportata e se le relative caratteristiche di prestazioni (larghezza di banda e latenza) sono note come la memoria dedicata (non condivisa).
È possibile determinare le proprietà dell'acceleratore predefinito creando l'acceleratore predefinito ed esaminandone le proprietà. Nell'esempio di codice seguente viene stampato il percorso, la quantità di memoria dell'acceleratore, il supporto della memoria condivisa, il supporto a precisione doppia e il supporto limitato a precisione doppia dell'acceleratore 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 l'oggetto accelerator::device_path
dell'acceleratore predefinito. Il percorso è dipendente dall'hardware. Il codice seguente usa la accelerator::get_all
funzione per recuperare un elenco degli 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, usare il accelerator::get_all
metodo per recuperare un elenco degli acceleratori disponibili e quindi selezionarne uno in base alle relative proprietà. Questo esempio mostra come selezionare l'acceleratore con la maggior quantità 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 di CPU. Non è possibile eseguire codice sull'acceleratore cpu. Per filtrare l'acceleratore cpu, confrontare il valore della proprietà device_path dell'acceleratore restituito da accelerator::get_all
con il valore dell'acceleratore::cpu_accelerator. Per altre informazioni, vedere la sezione "Acceleratori speciali" in questo articolo.
Shared Memory
La memoria condivisa è la memoria a cui è possibile accedere sia la CPU che l'acceleratore. L'uso della memoria condivisa elimina o riduce significativamente il sovraccarico di copia dei dati tra la CPU e l'acceleratore. Anche se la memoria è condivisa, non è possibile accedervi contemporaneamente sia dalla CPU che dall'acceleratore e in questo modo causa un comportamento indefinito. La proprietà dell'acceleratore supports_cpu_shared_memory restituisce true
se l'acceleratore supporta la memoria condivisa e la proprietà default_cpu_access_type ottiene il access_type predefinito per la accelerator
memoria allocata in , ad esempio la matriceassociata agli accelerator
oggetti o array_view
a cui si accede su accelerator
.
Il runtime C++ AMP sceglie automaticamente il valore predefinito access_type
migliore per ogni accelerator
, ma le caratteristiche delle prestazioni (larghezza di banda e latenza) della memoria condivisa possono essere peggiori rispetto a quelle della memoria dell'acceleratore dedicata (non condivisa) durante la lettura dalla CPU, la scrittura dalla CPU o entrambi. Se la memoria condivisa esegue e memoria dedicata per la lettura e la scrittura dalla CPU, il runtime usa access_type_read_write
per impostazione predefinita ; in caso contrario, il runtime sceglie un valore predefinito access_type
più conservativo e consente all'app di eseguirne l'override se i modelli di accesso alla memoria dei kernel di calcolo traggono vantaggio da un diverso access_type
.
Nell'esempio di codice seguente viene illustrato come determinare se l'acceleratore predefinito supporta la memoria condivisa e quindi esegue l'override del tipo di accesso predefinito e ne crea uno 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
oggetto riflette sempre l'oggetto default_cpu_access_type
dell'oggetto accelerator
a cui è associato e non fornisce alcuna interfaccia per eseguire l'override o modificare il relativo access_type
oggetto .
Modifica dell'acceleratore predefinito
È possibile modificare l'acceleratore predefinito chiamando il accelerator::set_default
metodo . È possibile modificare l'acceleratore predefinito una sola volta per ogni esecuzione dell'app ed è necessario modificarlo prima dell'esecuzione di qualsiasi codice nella GPU. Tutte le chiamate di funzione successive per modificare l'acceleratore restituiscono false
. Per usare un acceleratore diverso in una chiamata a parallel_for_each
, leggere la sezione "Uso di più acceleratori" in questo articolo. L'esempio di codice seguente imposta l'acceleratore predefinito su un acceleratore non emulato, non è connesso a uno schermo e supporta la precisione doppia.
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 di più acceleratori
Esistono due modi per usare più acceleratori nell'app:
È possibile passare
accelerator_view
oggetti alle chiamate al metodo parallel_for_each .È possibile costruire un oggetto matrice usando un oggetto specifico
accelerator_view
. Il runtime C+AMP rileverà l'oggettoaccelerator_view
dall'oggetto matrice acquisito nell'espressione lambda.
Acceleratori speciali
I percorsi dei dispositivi di tre acceleratori speciali sono disponibili come proprietà della accelerator
classe :
accelerator::d irect3d_ref Data Member: questo acceleratore a thread singolo usa software nella CPU per emulare una scheda grafica generica. Viene usato per impostazione predefinita per il debug, ma non è utile nell'ambiente di produzione perché è più lento rispetto agli acceleratori hardware. Inoltre, è disponibile solo in DirectX SDK e Windows SDK ed è improbabile che sia installato nei computer dei clienti. Per altre informazioni, vedere Debug del codice GPU.
accelerator::d irect3d_warp Data Member: questo acceleratore offre una soluzione di fallback per l'esecuzione di codice AMP C++ su CPU multi-core che usano estensioni SIMD di streaming (SSE).
accelerator::cpu_accelerator Membro dati: è possibile usare questo acceleratore per configurare le matrici di staging. Non è possibile eseguire codice C++ AMP. Per altre informazioni, vedere il post Relativo alle matrici di staging in C++ AMP nel blog programmazione parallela in codice nativo.
Interoperabilità
Il runtime C++ AMP supporta l'interoperabilità tra la accelerator_view
classe e l'interfaccia Direct3D ID3D11Device. Il metodo create_accelerator_view accetta un'interfaccia IUnknown
e restituisce un accelerator_view
oggetto . Il metodo get_device accetta un accelerator_view
oggetto e restituisce un'interfaccia IUnknown
.
Vedi anche
C++ AMP (C++ Accelerated Massive Parallelism)
Debug del codice GPU
Classe accelerator_view