Używanie akceleratora i obiektów accelerator_view
Można użyć klas accelerator i accelerator_view do określenia urządzenia lub emulatora do uruchamiania kodu C++ AMP.System może mieć kilka urządzeń lub emulatorów różniących się ilością pamięci, obsługą pamięci wspólnej i debugowania lub wsparciem dla podwójnej precyzji.Biblioteka C++ Accelerated Massive Parallelism (C++ AMP) udostępnia interfejsy API, które można wykorzystać do analizy dostępnych akceleratorów, ustawienie jednego z nich jako domyślny, określenia wielu accelerator_views dla wielu wywołań parallel_for_each oraz wykonywania specjalnych zadań debugowania.
Używanie akceleratora domyślnego
Środowisko wykonawcze C++ AMP wybiera akcelerator domyślny, o ile nie zostanie napisany kod wybierający konkretny akcelerator.Środowisko wykonawcze wybiera akcelerator domyślny w następujący sposób:
Jeśli aplikacja jest uruchomiona w trybie debugowania, akcelerator obsługuje debugowanie.
W przeciwnym razie akcelerator, który jest określony przez zmienną środowiskową CPPAMP_DEFAULT_ACCELERATOR, jeśli jest ustawiona.
W przeciwnym razie urządzenie nie jest 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świetlania.
Ponadto środowisko wykonawcze określa access_type z access_type_auto dla akceleratora domyślnego.Oznacza to, że akcelerator domyślny używa pamięci współużytkowanej, jeśli jest obsługiwany i jeśli jego charakterystyka wydajności (przepustowość i czas oczekiwania) są znane jako takie same, jak te dla dedykowanej pamięci (nieudostępnianej).
Można określić właściwości domyślnego akceleratora przez skonstruowanie domyślnego akceleratora i zbadanie jego właściwości.Poniższy przykład kodu drukuje ścieżkę, ilość pamięci akceleratora, obsługę pamięci współdzielonej, obsługę podwójnej precyzji i obsługę ograniczonej podwójnej precyzji 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 akceleratora domyślnego.Ścieżka jest zależna od sprzętu.Następujący kod używa funkcji accelerator::get_all, aby pobrać listę dostępnych akceleratorów, a następnie wyświetlić ś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 metody accelerator::get_all do pobrania listy dostępnych akceleratorów, a następnie wybierz jeden na podstawie jego właściwości.Przykład pokazuje, jak wybrać akcelerator, który ma najwięcej 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]
Jeden z akceleratorów, które są zwracane przez accelerator::get_all, to akcelerator procesora CPU.Nie można wykonywać kodu na akceleratorze procesora.Aby odfiltrować accelerator procesora CPU, porównaj wartość właściwości akceleratora device_path, który jest zwracany przez accelerator::get_all z wartością accelerator::cpu_accelerator.Aby uzyskać więcej informacji zobacz sekcję „Specjalne akceleratory” w niniejszym artykule.
Pamięci wspólna
Pamięć współużytkowana jest pamięcią, do której ma dostęp zarówno Procesor, jak i akcelerator.Wykorzystanie pamięci współdzielonej eliminuje lub znacznie zmniejsza obciążenie kopiowania danych między procesorem a akceleratorem.Mimo,że pamięć jest udostępniona, nie może uzyskać do niej dostępu ani procesor, ani akcelerator, a czynność ta powoduje niezdefiniowane zachowanie.Właściwość akceleratora supports_cpu_shared_memory zwraca true jeśli akcelerator obsługuje pamięci współużytkowane i właściwość default_cpu_access_type pobiera domyślny access_type dla pamięci przydzielonej na accelerator— na przykład, arrayzwiązane z obiektami accelerator, lub array_view z dostępem z accelerator.
Środowisko wykonawcze języka C++ AMP automatycznie wybiera najlepsze domyślne access_type dla każdego accelerator, ale cechy wydajności (przepustowość i czas oczekiwania) pamięci współużytkowanej mogą być gorsze niż w przypadku dedykowanego (nieudostępnianego) akceleratora pamięci podczas odczytu z CPU, zapisu z CPU lub dla obu tych przypadków.Jeśli pamięć współużytkowana działa równie dobrze jak dedykowana pamięć dla odczytu i zapisu z procesora, środowisko uruchomieniowe domyślnie przyjmuje tryb access_type_read_write; w przeciwnym wypadku środowisko wykonawcze wybiera bardziej ostrożny domyślny tryb access_type i pozwala aplikacji go zastąpić, jeśli wzorce dostępu do pamięci jej jąder obliczeniowych odniosą korzyści z innego trybu access_type.
Poniższy przykład kodu pokazuje, jak ustalić, czy akcelerator domyślny obsługuje pamięć współużytkowaną, a następnie zastępuje domyślny typ dostępu i tworzy z niego 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;
}
accelerator_view zawsze odzwierciedla default_cpu_access_type z accelerator, jest z nim skojarzony i zapewnia interfejs do nadpisania lub jej zmiany access_type.
Zmienianie akceleratora domyślnego
Akcelerator domyślny można zmienić, wywołując metodę accelerator::set_default.Akcelerator domyślny można zmienić tylko raz na wykonanie aplikacji i należy go zmienić przed wykonaniem jakiegokolwiek kodu na procesorze GPU.Wszystkie kolejne wywołania funkcji zmiany akceleratora zwracają wartośćfalse.Jeśli chcesz użyć innego akceleratora w wywołaniu parallel_for_each, przeczytaj sekcję „Używanie wielu akceleratorów” w niniejszym artykule.Poniższy przykład kodu ustawia akcelerator domyślny, który nie jest emulowany, nie jest podłączony do wyświetlania 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
Można użyć wielu akceleratorów w aplikacji na dwa sposoby:
Można przekazać obiekty accelerator_view do wywołań metody parallel_for_each.
Można skonstruować obiekt array za pomocą określonego obiektu accelerator_view.Środowisko wykonawcze C+AMP wybierze obiekt accelerator_view z przechwyconych obiektów array w wyrażeniu lambda.
Akceleratory specjalne
Ścieżki urządzenia dla trzech akceleratorów specjalnych są dostępne jako właściwości klasy accelerator:
accelerator::direct3d_ref — Członek danych: Ten akcelerator jednowątkowy używa oprogramowania CPU do emulowania rodzajowej karty graficznej.Jest używany domyślnie do debugowania, ale nie jest użyteczny podczas produkcji, ponieważ jest wolniejszy niż akceleratory sprzętowe.Ponadto jest dostępny tylko w zestawie DirectX SDK i SDK systemu Windows i jest mało prawdopodobne, aby był zainstalowany na komputerach klientów.Aby uzyskać więcej informacji, zobacz Debugowanie kodu GPU.
accelerator::direct3d_warp — Członek danych: Akcelerator ten dostarcza rozwiązanie alternatywne do wykonywania kodu C++ AMP na wielordzeniowych procesorach, które używają rozszerzenia SSE (Streaming SIMD).
accelerator::cpu_accelerator — Członek danych: Można wykorzystać ten akcelerator do tworzenia tablic tymczasowych.Nie może wykonywać kodu C++ AMP.Aby uzyskać więcej informacji, zobacz wpis Tablice tymczasowe w bibliotece C++ AMP na blogu dotyczącym programowania równoległego w kodzie natywnym.
Współdziałanie
Środowisko wykonawcze języka C++ AMP wspiera współdziałanie między klasą accelerator_view a występującym w Direct3D interfejsem ID3D11Device.Metoda create_accelerator_view pobiera interfejs IUnknown i zwraca obiekt accelerator_view.Metoda get_device pobiera obiekt accelerator_view i zwraca interfejs IUknown.