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:

  1. Jeśli aplikacja działa w trybie debugowania, akcelerator obsługujący debugowanie.

  2. W przeciwnym razie akcelerator określony przez zmienną środowiskową CPPAMP_DEFAULT_ACCELERATOR , jeśli jest ustawiony.

  3. W przeciwnym razie urządzenie nie emulowane.

  4. W przeciwnym razie urządzenie, które ma największą ilość dostępnej pamięci.

  5. W przeciwnym razie urządzenie, które nie jest dołączone do wyświetlacza.

Ponadto środowisko uruchomieniowe określa wartość access_typeaccess_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 acceleratorobiekcie — na przykład tablicyskojarzonej z obiektami acceleratorlub array_view dostępnymi na obiekcie accelerator.

Środowisko uruchomieniowe C++ AMP automatycznie wybiera najlepszą wartość domyślną access_type dla każdego acceleratorobiektu , 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_typei umożliwia aplikacji zastąpienie go, jeśli wzorce dostępu do pamięci jej jądra obliczeniowego korzystają z innego access_typeelementu .

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_typeelementu .

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 pobiera accelerator_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