Używanie akceleratora i obiektów accelerator_view
Możesz użyć akceleratora i klas accelerator_view, aby określić urządzenie lub emulator do uruchomienia kodu AMP języka C++. System może mieć kilka urządzeń lub emulatorów, które różnią się ilością pamięci, obsługą pamięci udostępnionej, obsługą debugowania lub obsługą podwójnej precyzji. C++ Accelerated Massive Parallelism (C++ AMP) udostępnia interfejsy API, których można użyć do zbadania dostępnych akceleratorów, ustawić jeden jako domyślny, określić wiele accelerator_views dla wielu wywołań do parallel_for_each i wykonać specjalne zadania debugowania.
Uwaga
Nagłówki C++ AMP są przestarzałe, począwszy od programu Visual Studio 2022 w wersji 17.0.
Dołączenie wszystkich nagłówków AMP spowoduje wygenerowanie błędów kompilacji. Zdefiniuj _SILENCE_AMP_DEPRECATION_WARNINGS
przed dołączeniem żadnych nagłówków AMP, aby wyciszyć ostrzeżenia.
Korzystanie z akceleratora domyślnego
Środowisko uruchomieniowe C++ AMP wybiera domyślny akcelerator, chyba że napiszesz kod w celu wybrania określonego. Środowisko uruchomieniowe wybiera domyślny akcelerator w następujący sposób:
Jeśli aplikacja działa w trybie debugowania, akcelerator obsługujący debugowanie.
W przeciwnym razie akcelerator określony przez zmienną środowiskową
CPPAMP_DEFAULT_ACCELERATOR
, jeśli jest ustawiony.W przeciwnym razie urządzenie nie emulowane.
W przeciwnym razie urządzenie, które ma największą ilość dostępnej pamięci.
W przeciwnym razie urządzenie, które nie jest dołączone do wyświetlacza.
Ponadto środowisko uruchomieniowe określa wartość access_type
access_type_auto
dla domyślnego akceleratora. Oznacza to, że domyślny akcelerator używa pamięci udostępnionej, jeśli jest obsługiwana, a jego charakterystykę wydajności (przepustowość i opóźnienie) są znane jako pamięć dedykowana (nieudzielona).
Właściwości domyślnego akceleratora można określić, tworząc domyślny akcelerator i sprawdzając jego właściwości. Poniższy przykład kodu wyświetla ścieżkę, ilość pamięci akceleratora, obsługę pamięci udostępnionej, obsługę podwójnej precyzji i ograniczoną precyzję obsługi akceleratora domyślnego.
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";
}
zmienna środowiskowa CPPAMP_DEFAULT_ACCELERATOR
Można ustawić zmienną środowiskową CPPAMP_DEFAULT_ACCELERATOR, aby określić accelerator::device_path
domyślny akcelerator. Ścieżka jest zależna od sprzętu. Poniższy kod używa accelerator::get_all
funkcji do pobrania listy dostępnych akceleratorów, a następnie wyświetla ścieżkę i charakterystykę każdego akceleratora.
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";
}
}
Wybieranie akceleratora
Aby wybrać akcelerator, użyj accelerator::get_all
metody , aby pobrać listę dostępnych akceleratorów, a następnie wybrać jeden na podstawie jego właściwości. W tym przykładzie pokazano, jak wybrać akcelerator, który ma największą ilość pamięci:
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";
}
Uwaga
Jednym z akceleratorów zwracanych przez accelerator::get_all
program jest akcelerator procesora CPU. Nie można wykonać kodu w akceleratorze procesora. Aby odfiltrować akcelerator procesora CPU, porównaj wartość właściwości device_path akceleratora zwróconego przez accelerator::get_all
wartość akceleratora ::cpu_accelerator. Aby uzyskać więcej informacji, zobacz sekcję "Akceleratory specjalne" w tym artykule.
Pamięć współdzielona
Pamięć współdzielona to pamięć, do którego można uzyskać dostęp zarówno przez procesor CPU, jak i akcelerator. Użycie pamięci udostępnionej eliminuje lub znacznie zmniejsza nakład pracy związany z kopiowaniem danych między procesorem CPU a akceleratorem. Mimo że pamięć jest współdzielona, nie można uzyskać do niej dostępu współbieżnie zarówno przez procesor, jak i akcelerator, i powoduje to niezdefiniowane zachowanie. Właściwość akceleratora supports_cpu_shared_memory zwraca true
wartość, jeśli akcelerator obsługuje pamięć współdzieloną, a właściwość default_cpu_access_type pobiera domyślną access_type dla pamięci przydzielonej na accelerator
obiekcie — na przykład tablicyskojarzonej z obiektami accelerator
lub array_view
dostępnymi na obiekcie accelerator
.
Środowisko uruchomieniowe C++ AMP automatycznie wybiera najlepszą wartość domyślną access_type
dla każdego accelerator
obiektu , ale charakterystyki wydajności (przepustowość i opóźnienie) pamięci udostępnionej mogą być gorsze niż w przypadku pamięci dedykowanej (nieudostępnej) akceleratora podczas odczytywania z procesora CPU, zapisywania z procesora CPU lub obu tych typów. Jeśli pamięć współdzielona działa, a także dedykowana pamięć do odczytu i zapisu z procesora CPU, środowisko uruchomieniowe domyślnie domyślnie access_type_read_write
; w przeciwnym razie środowisko uruchomieniowe wybiera bardziej konserwatywną wartość domyślną access_type
i umożliwia aplikacji zastąpienie go, jeśli wzorce dostępu do pamięci jej jądra obliczeniowego korzystają z innego access_type
elementu .
W poniższym przykładzie kodu pokazano, jak określić, czy domyślny akcelerator obsługuje pamięć udostępnioną, a następnie zastępuje domyślny typ dostępu i tworzy z niego obiekt 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;
}
Element accelerator_view
zawsze odzwierciedla default_cpu_access_type
element accelerator
skojarzony z nim i nie zapewnia interfejsu do zastąpienia ani zmiany jego access_type
elementu .
Zmienianie akceleratora domyślnego
Domyślny akcelerator można zmienić, wywołując metodę accelerator::set_default
. Możesz zmienić domyślny akcelerator tylko raz na wykonanie aplikacji i zmienić go przed wykonaniem jakiegokolwiek kodu na procesorze GPU. Wszystkie kolejne wywołania funkcji w celu zmiany akceleratora zwracają wartość false
. Jeśli chcesz użyć innego akceleratora w wywołaniu metody parallel_for_each
, przeczytaj sekcję "Korzystanie z wielu akceleratorów" w tym artykule. Poniższy przykład kodu ustawia domyślny akcelerator na taki, który nie jest emulowany, nie jest połączony z wyświetlaczem i obsługuje podwójną precyzję.
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;
}
Używanie wielu akceleratorów
Istnieją dwa sposoby używania wielu akceleratorów w aplikacji:
Obiekty można przekazać
accelerator_view
do wywołań metody parallel_for_each .Obiekt tablicy można skonstruować przy użyciu określonego
accelerator_view
obiektu. Środowisko uruchomieniowe C+AMP pobieraaccelerator_view
obiekt z przechwyconego obiektu tablicy w wyrażeniu lambda.
Akceleratory specjalne
Ścieżki urządzeń trzech specjalnych akceleratorów są dostępne jako właściwości accelerator
klasy:
accelerator::d irect3d_ref Data Member: ten akcelerator jednowątkowy używa oprogramowania na procesorze w celu emulowania ogólnej karty graficznej. Jest ona używana domyślnie do debugowania, ale nie jest przydatna w środowisku produkcyjnym, ponieważ jest wolniejsza niż akceleratory sprzętowe. Ponadto jest ona dostępna tylko w zestawie SDK DirectX i zestawie Windows SDK i prawdopodobnie nie zostanie zainstalowana na komputerach klientów. Aby uzyskać więcej informacji, zobacz Debugowanie kodu procesora GPU.
accelerator::d irect3d_warp Data Member: Ten akcelerator udostępnia rozwiązanie rezerwowe do wykonywania kodu C++ AMP na procesorach wielordzeniowych korzystających z rozszerzeń SIMD przesyłania strumieniowego (SSE).
accelerator::cpu_accelerator Element członkowski danych: możesz użyć tego akceleratora do konfigurowania tablic przejściowych. Nie może wykonać kodu C++ AMP. Aby uzyskać więcej informacji, zobacz wpis Staging Arrays in C++ AMP (Tablice przejściowe w języku C++ AMP ) w blogu Parallel Programming in Native Code (Programowanie równoległe w kodzie natywnym).
Współdziałanie
Środowisko uruchomieniowe C++ AMP obsługuje współdziałanie klasy accelerator_view
i interfejsu Direct3D ID3D11Device. Metoda create_accelerator_view przyjmuje IUnknown
interfejs i zwraca accelerator_view
obiekt. Metoda get_device przyjmuje accelerator_view
obiekt i zwraca IUnknown
interfejs.
Zobacz też
C++ AMP (C++ Accelerated Massive Parallelism)
Debugowanie kodu GPU
accelerator_view, klasa