Sdílet prostřednictvím


Přehled produktu C++ AMP

Poznámka:

Hlavičky C++ AMP jsou zastaralé od sady Visual Studio 2022 verze 17.0. Zahrnutím všech hlaviček AMP se vygenerují chyby sestavení. Před zahrnutím záhlaví AMP definujte _SILENCE_AMP_DEPRECATION_WARNINGS upozornění.

Akcelerovaný masivní paralelismus C++ (C++ AMP) urychluje provádění kódu C++ tím, že využívá hardware paralelních dat, jako je grafický procesor (GPU) na diskrétní grafické kartě. Pomocí C++ AMP můžete kódovat multidimenzionální datové algoritmy, aby bylo možné provádění urychlit pomocí paralelismu na heterogenním hardwaru. Programovací model C++ AMP zahrnuje multidimenzionální pole, indexování, přenos paměti, svázání a matematickou knihovnu funkcí. Pomocí jazykových rozšíření C++ AMP můžete řídit, jak se data přesouvají z procesoru do GPU a zpět, abyste mohli zvýšit výkon.

Požadavky na systém

  • Windows 7 nebo novější

  • Windows Server 2008 R2 až Visual Studio 2019.

  • Hardware directX 11 úrovně funkce 11.0 nebo novější

  • Pro ladění v emulátoru softwaru se vyžaduje Windows 8 nebo Windows Server 2012. Pro ladění na hardwaru je nutné nainstalovat ovladače grafické karty. Další informace najdete v tématu Ladění kódu GPU.

  • Poznámka: AMP se v současné době nepodporuje v ARM64.

Úvod

Následující dva příklady ilustrují primární komponenty C++ AMP. Předpokládejme, že chcete přidat odpovídající prvky dvou jednorozměrných polí. Můžete například chtít přidat {1, 2, 3, 4, 5} a {6, 7, 8, 9, 10} získat {7, 9, 11, 13, 15}. Bez použití C++ AMP můžete napsat následující kód, který přidá čísla a zobrazí výsledky.

#include <iostream>

void StandardMethod() {

    int aCPP[] = {1, 2, 3, 4, 5};
    int bCPP[] = {6, 7, 8, 9, 10};
    int sumCPP[5];

    for (int idx = 0; idx < 5; idx++)
    {
        sumCPP[idx] = aCPP[idx] + bCPP[idx];
    }

    for (int idx = 0; idx < 5; idx++)
    {
        std::cout << sumCPP[idx] << "\n";
    }
}

Důležité části kódu jsou následující:

  • Data: Data se skládají ze tří polí. Všechny mají stejné pořadí (jedno) a délku (pět).

  • Iterace: První for smyčka poskytuje mechanismus iterace prostřednictvím prvků v polích. Kód, který chcete provést pro výpočet součtů, je obsažen v prvním for bloku.

  • Index: Proměnná idx přistupuje k jednotlivým prvkům polí.

Pomocí C++ AMP můžete místo toho napsat následující kód.

#include <amp.h>
#include <iostream>
using namespace concurrency;

const int size = 5;

void CppAmpMethod() {
    int aCPP[] = {1, 2, 3, 4, 5};
    int bCPP[] = {6, 7, 8, 9, 10};
    int sumCPP[size];

    // Create C++ AMP objects.
    array_view<const int, 1> a(size, aCPP);
    array_view<const int, 1> b(size, bCPP);
    array_view<int, 1> sum(size, sumCPP);
    sum.discard_data();

    parallel_for_each(
        // Define the compute domain, which is the set of threads that are created.
        sum.extent,
        // Define the code to run on each thread on the accelerator.
        [=](index<1> idx) restrict(amp) {
            sum[idx] = a[idx] + b[idx];
        }
    );

    // Print the results. The expected output is "7, 9, 11, 13, 15".
    for (int i = 0; i < size; i++) {
        std::cout << sum[i] << "\n";
    }
}

Existují stejné základní prvky, ale používají se konstrukty C++ AMP:

  • Data: Pomocí polí C++ můžete vytvořit tři objekty C++ AMP array_view . Zadáte čtyři hodnoty pro vytvoření objektu array_view : datové hodnoty, pořadí, typ prvku a délku objektu array_view v každé dimenzi. Pořadí a typ se předávají jako parametry typu. Data a délka se předávají jako parametry konstruktoru. V tomto příkladu je pole C++, které je předáno konstruktoru, jednorozměrné. Pořadí a délka slouží k vytvoření obdélníkového tvaru dat v objektu array_view a datové hodnoty slouží k vyplnění pole. Knihovna modulu runtime obsahuje také třídu pole, která má rozhraní, které se podobá array_view třídě a je popsáno dále v tomto článku.

  • Iterace: Funkce parallel_for_each (C++ AMP) poskytuje mechanismus iterace prostřednictvím datových prvků nebo výpočetní domény. V tomto příkladu je výpočetní doména určena parametrem sum.extent. Kód, který chcete spustit, je obsažen ve výrazu lambda nebo ve funkci jádra. Označuje restrict(amp) , že se používá pouze podmnožina jazyka C++, kterou může C++ AMP zrychlit.

  • Index: Proměnná třídy indexu , je deklarována s pořadím jednoho, který odpovídá pořadí objektu array_view idx. Pomocí indexu můžete přistupovat k jednotlivým prvkům array_view objektů.

Strukturování a indexování dat: index a rozsah

Před spuštěním kódu jádra musíte definovat hodnoty dat a deklarovat tvar dat. Všechna data jsou definována jako matice (obdélníková) a můžete definovat pole tak, aby měla libovolné pořadí (počet dimenzí). Data můžou mít libovolnou velikost v libovolné dimenzi.

index – třída

Třída indexu určuje umístění v objektu array zapouzdřením array_view posunu od původu v každé dimenzi do jednoho objektu. Když přistupujete k umístění v poli, předáte index objekt operátoru indexování , []místo seznamu celočíselné indexy. K prvkům v jednotlivých dimenzích můžete přistupovat pomocí operátoru array::operator() nebo operátoru array_view::operator().

Následující příklad vytvoří jednorozměrný index, který určuje třetí prvek v jednorozměrném array_view objektu. Index se používá k tisku třetího prvku v objektu array_view . Výstup je 3.

int aCPP[] = {1, 2, 3, 4, 5};
array_view<int, 1> a(5, aCPP);

index<1> idx(2);

std::cout << a[idx] << "\n";
// Output: 3

Následující příklad vytvoří dvojrozměrný index, který určuje prvek, kde řádek = 1 a sloupec = 2 v dvojrozměrném array_view objektu. Prvním parametrem v konstruktoru index je součást řádku a druhým parametrem je komponenta sloupce. Výstup je 6.

int aCPP[] = {1, 2, 3, 4, 5, 6};
array_view<int, 2> a(2, 3, aCPP);

index<2> idx(1, 2);

std::cout <<a[idx] << "\n";
// Output: 6

Následující příklad vytvoří trojrozměrný index, který určuje prvek, kde hloubka = 0, řádek = 1 a sloupec = 3 v trojrozměrném array_view objektu. Všimněte si, že prvním parametrem je součást hloubky, druhým parametrem je součást řádku a třetí parametr je komponenta sloupce. Výstup je 8.

int aCPP[] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

array_view<int, 3> a(2, 3, 4, aCPP);

// Specifies the element at 3, 1, 0.
index<3> idx(0, 1, 3);

std::cout << a[idx] << "\n";
// Output: 8

extent – třída

Třída rozsahu určuje délku dat v každé dimenzi objektuarray.array_view Můžete vytvořit rozsah a použít ho k vytvoření objektuarray.array_view Můžete také načíst rozsah existujícího array objektu nebo array_view objektu. Následující příklad vytiskne délku rozsahu v každé dimenzi objektu array_view .

int aCPP[] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
// There are 3 rows and 4 columns, and the depth is two.
array_view<int, 3> a(2, 3, 4, aCPP);

std::cout << "The number of columns is " << a.extent[2] << "\n";
std::cout << "The number of rows is " << a.extent[1] << "\n";
std::cout << "The depth is " << a.extent[0] << "\n";
std::cout << "Length in most significant dimension is " << a.extent[0] << "\n";

Následující příklad vytvoří array_view objekt, který má stejné dimenze jako objekt v předchozím příkladu, ale tento příklad používá extent objekt místo použití explicitních parametrů v konstruktoru array_view .

int aCPP[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
extent<3> e(2, 3, 4);

array_view<int, 3> a(e, aCPP);

std::cout << "The number of columns is " << a.extent[2] << "\n";
std::cout << "The number of rows is " << a.extent[1] << "\n";
std::cout << "The depth is " << a.extent[0] << "\n";

Přesun dat do akcelerátoru: pole a array_view

V knihovně modulu runtime jsou definovány dva kontejnery dat sloužící k přesunu dat do akcelerátoru. Jedná se o třídu pole a třídu array_view. Třída array je třída kontejneru, která při vytváření objektu vytvoří hlubokou kopii dat. Třída array_view je obálková třída, která kopíruje data, když funkce jádra přistupuje k datům. Pokud jsou data potřebná na zdrojovém zařízení, data se zkopírují zpět.

array – třída

Při vytváření objektu array se v akcelerátoru vytvoří hloubková kopie dat, pokud použijete konstruktor, který obsahuje ukazatel na datovou sadu. Funkce jádra upraví kopii akcelerátoru. Po dokončení provádění funkce jádra je nutné zkopírovat data zpět do struktury zdrojových dat. Následující příklad vynásobí každý prvek vektoru o 10. Po dokončení vector conversion operator funkce jádra se použije ke zkopírování dat zpět do vektorového objektu.

std::vector<int> data(5);

for (int count = 0; count <5; count++)
{
    data[count] = count;
}

array<int, 1> a(5, data.begin(), data.end());

parallel_for_each(
    a.extent,
    [=, &a](index<1> idx) restrict(amp) {
        a[idx] = a[idx]* 10;
    });

data = a;
for (int i = 0; i < 5; i++)
{
    std::cout << data[i] << "\n";
}

array_view – třída

array_view téměř stejné členy jako array třída, ale základní chování není stejné. Data předaná konstruktoru array_view se na GPU nereplikují, protože se jedná o array konstruktor. Místo toho se data zkopírují do akcelerátoru při spuštění funkce jádra. Proto pokud vytvoříte dva array_view objekty, které používají stejná data, oba array_view objekty odkazují na stejný prostor paměti. Když to uděláte, musíte synchronizovat jakýkoli přístup s více vlákny. Hlavní výhodou použití array_view třídy je, že data se přesunou pouze v případě potřeby.

Porovnání polí a array_view

Následující tabulka shrnuje podobnosti a rozdíly mezi třídamiarray.array_view

Popis array – třída array_view – třída
Při určení pořadí V době kompilace. V době kompilace.
Při určení rozsahu Za běhu. Za běhu.
Tvar Obdélníkový. Obdélníkový.
Úložiště dat Je datový kontejner. Je to obálka dat.
Kopírovat Explicitní a hloubková kopie v definici Implicitní kopírování při přístupu k funkci jádra.
Načtení dat Zkopírováním dat pole zpět do objektu ve vlákně procesoru. Přímým přístupem k objektu array_view nebo voláním metody array_view::synchronize pokračujte v přístupu k datům v původním kontejneru.

Sdílená paměť s polem a array_view

Sdílená paměť je paměť, ke které má přístup procesor i akcelerátor. Použití sdílené paměti eliminuje nebo výrazně snižuje režii při kopírování dat mezi procesorem a akcelerátorem. I když je paměť sdílená, nelze k ní přistupovat současně procesorem i akcelerátorem a tím způsobit nedefinované chování.

array objekty lze použít k určení jemně odstupňované kontroly nad používáním sdílené paměti, pokud ho přidružený akcelerátor podporuje. Určuje, zda akcelerátor podporuje sdílenou paměť, určuje vlastnost supports_cpu_shared_memory akcelerátoru, která se vrátí true při podpoře sdílené paměti. Pokud je podporována sdílená paměť, výchozí access_type Výčet přidělení paměti v akcelerátoru default_cpu_access_type je určen vlastností. Ve výchozím nastavení array a array_view objekty se zabírají stejně access_type jako primární přidružené accelerator.

Nastavením vlastnosti array array::cpu_access_type Data Member explicitně můžete provést jemně odstupňovanou kontrolu nad tím, jak se sdílená paměť používá, abyste mohli optimalizovat aplikaci pro charakteristiky výkonu hardwaru na základě vzorů přístupu k paměti jeho výpočetních jader. Odráží array_view stejné jako cpu_access_type array to, ke kterému je přidruženo; nebo pokud je array_view vytvořen bez zdroje dat, odráží jeho access_type prostředí, které ho nejprve způsobí přidělení úložiště. To znamená, že pokud je poprvé přístupný hostitelem (procesorem), chová se tak, jako by byl vytvořen přes zdroj dat procesoru a sdílí access_type přidruženou funkci accelerator_view capture. Pokud je ale poprvé přístupná hostitelem accelerator_view, chová se, jako by byla vytvořená prostřednictvím vytvořeného array accelerator_view zdroje dat a sdílí arrayje access_type.

Následující příklad kódu ukazuje, jak určit, jestli výchozí akcelerátor podporuje sdílenou paměť, a pak vytvoří několik polí s různými konfiguracemi cpu_access_type.

#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.default_cpu_access_type = access_type_read_write

    // Create an accelerator_view from the default accelerator. The
    // accelerator_view inherits its default_cpu_access_type from acc.
    accelerator_view acc_v = acc.default_view;

    // Create an extent object to size the arrays.
    extent<1> ex(10);

    // Input array that can be written on the CPU.
    array<int, 1> arr_w(ex, acc_v, access_type_write);

    // Output array that can be read on the CPU.
    array<int, 1> arr_r(ex, acc_v, access_type_read);

    // Read-write array that can be both written to and read from on the CPU.
    array<int, 1> arr_rw(ex, acc_v, access_type_read_write);
}

Provádění kódu přes data: parallel_for_each

Funkce parallel_for_each definuje kód, který chcete spustit na akcelerátoru proti datům v objektu nebo array_view objektuarray. Podívejte se na následující kód z úvodu tohoto tématu.

#include <amp.h>
#include <iostream>
using namespace concurrency;

void AddArrays() {
    int aCPP[] = {1, 2, 3, 4, 5};
    int bCPP[] = {6, 7, 8, 9, 10};
    int sumCPP[5] = {0, 0, 0, 0, 0};

    array_view<int, 1> a(5, aCPP);
    array_view<int, 1> b(5, bCPP);
    array_view<int, 1> sum(5, sumCPP);

    parallel_for_each(
        sum.extent,
        [=](index<1> idx) restrict(amp)
        {
            sum[idx] = a[idx] + b[idx];
        }
    );

    for (int i = 0; i < 5; i++) {
        std::cout << sum[i] << "\n";
    }
}

Metoda parallel_for_each přebírá dva argumenty, výpočetní doménu a výraz lambda.

Výpočetní doména je extent objekt nebo tiled_extent objekt, který definuje sadu vláken, která se má vytvořit pro paralelní spuštění. Pro každý prvek ve výpočetní doméně se vygeneruje jedno vlákno. V tomto případě extent je objekt jednorozměrný a má pět prvků. Proto se spustí pět vláken.

Výraz lambda definuje kód, který se má spustit v každém vlákně. Klauzule capture určuje, [=]že tělo výrazu lambda přistupuje ke všem zachyceným proměnným hodnotou, které jsou av tomto případě , ba sum. V tomto příkladu vytvoří seznam parametrů jednorozměrnou index proměnnou s názvem idx. Hodnota idx[0] 0 v prvním vlákně a v každém dalším vlákně se zvýší o jeden. Označuje restrict(amp) , že se používá pouze podmnožina jazyka C++, kterou může C++ AMP zrychlit. Omezení funkcí, které mají modifikátor omezení, jsou popsána v omezení (C++ AMP). Další informace najdete v tématu Syntaxe výrazu lambda.

Výraz lambda může obsahovat kód, který se má spustit, nebo může volat samostatnou funkci jádra. Funkce jádra musí obsahovat restrict(amp) modifikátor. Následující příklad je ekvivalentní předchozímu příkladu, ale volá samostatnou funkci jádra.

#include <amp.h>
#include <iostream>
using namespace concurrency;

void AddElements(
    index<1> idx,
    array_view<int, 1> sum,
    array_view<int, 1> a,
    array_view<int, 1> b) restrict(amp) {
    sum[idx] = a[idx] + b[idx];
}

void AddArraysWithFunction() {

    int aCPP[] = {1, 2, 3, 4, 5};
    int bCPP[] = {6, 7, 8, 9, 10};
    int sumCPP[5] = {0, 0, 0, 0, 0};

    array_view<int, 1> a(5, aCPP);
    array_view<int, 1> b(5, bCPP);
    array_view<int, 1> sum(5, sumCPP);

    parallel_for_each(
        sum.extent,
        [=](index<1> idx) restrict(amp) {
            AddElements(idx, sum, a, b);
        }
    );

    for (int i = 0; i < 5; i++) {
        std::cout << sum[i] << "\n";
    }
}

Zrychlení kódu: Dlaždice a bariéry

Další zrychlení můžete získat pomocí provazování. Svázání rozdělí vlákna na stejné obdélníkové podmnožiny nebo dlaždice. Na základě sady dat a algoritmu, který kódujete, určíte odpovídající velikost dlaždice. Pro každé vlákno máte přístup k globálnímu umístění datového prvku vzhledem k celku array nebo array_view k místnímu umístění vzhledem k dlaždici. Použití hodnoty místního indexu zjednodušuje váš kód, protože nemusíte psát kód pro překlad hodnot indexu z globálního na místní. Pokud chcete použít provázání, zavolejte metodu extent::tile ve výpočetní doméně v parallel_for_each metodě a použijte objekt tiled_index ve výrazu lambda.

V typických aplikacích se prvky na dlaždici nějakým způsobem vztahují a kód musí přistupovat k hodnotám na dlaždici a sledovat je. K tomu použijte klíčové slovo tile_static Klíčové slovo a tile_barrier::wait Metoda. Proměnná s klíčovým slovem tile_static má obor na celou dlaždici a pro každou dlaždici se vytvoří instance proměnné. Je nutné zpracovat synchronizaci přístupu k podprocesu dlaždic k proměnné. Metoda tile_barrier::wait zastaví provádění aktuálního vlákna, dokud všechna vlákna na dlaždici nedosáhly volání tile_barrier::wait. Hodnoty na dlaždici tak můžete hromadit pomocí tile_static proměnných. Pak můžete dokončit všechny výpočty, které vyžadují přístup ke všem hodnotám.

Následující diagram představuje dvojrozměrné pole vzorkovacích dat uspořádaných do dlaždic.

Hodnoty indexu v dlaždicovém rozsahu

Následující příklad kódu používá data vzorkování z předchozího diagramu. Kód nahradí každou hodnotu v dlaždici průměrem hodnot v dlaždici.

// Sample data:
int sampledata[] = {
    2, 2, 9, 7, 1, 4,
    4, 4, 8, 8, 3, 4,
    1, 5, 1, 2, 5, 2,
    6, 8, 3, 2, 7, 2};

// The tiles:
// 2 2    9 7    1 4
// 4 4    8 8    3 4
//
// 1 5    1 2    5 2
// 6 8    3 2    7 2

// Averages:
int averagedata[] = {
    0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0,
};

array_view<int, 2> sample(4, 6, sampledata);

array_view<int, 2> average(4, 6, averagedata);

parallel_for_each(
    // Create threads for sample.extent and divide the extent into 2 x 2 tiles.
    sample.extent.tile<2,2>(),
        [=](tiled_index<2,2> idx) restrict(amp) {
        // Create a 2 x 2 array to hold the values in this tile.
        tile_static int nums[2][2];

        // Copy the values for the tile into the 2 x 2 array.
        nums[idx.local[1]][idx.local[0]] = sample[idx.global];

        // When all the threads have executed and the 2 x 2 array is complete, find the average.
        idx.barrier.wait();
        int sum = nums[0][0] + nums[0][1] + nums[1][0] + nums[1][1];

        // Copy the average into the array_view.
        average[idx.global] = sum / 4;
    });

for (int i = 0; i <4; i++) {
    for (int j = 0; j <6; j++) {
        std::cout << average(i,j) << " ";
    }
    std::cout << "\n";
}

// Output:
// 3 3 8 8 3 3
// 3 3 8 8 3 3
// 5 5 2 2 4 4
// 5 5 2 2 4 4

Matematické knihovny

C++ AMP obsahuje dvě matematické knihovny. Knihovna s dvojitou přesností v concurrency::p recise_math Namespace poskytuje podporu pro funkce s dvojitou přesností. Poskytuje také podporu pro funkce s jednoduchou přesností, i když podpora dvojité přesnosti na hardwaru je stále nutná. Odpovídá specifikaci C99 (ISO/IEC 9899). Akcelerátor musí podporovat plnou dvojitou přesnost. Můžete určit, jestli ano, tak, že zkontrolujete hodnotu akcelerátoru ::supports_double_precision datový člen. Rychlá matematická knihovna v concurrency::fast_math Namespace obsahuje další sadu matematických funkcí. Tyto funkce, které podporují pouze float operandy, se spouštějí rychleji, ale nejsou tak přesné jako ty v matematické knihovně s dvojitou přesností. Funkce jsou obsaženy v souboru hlavičky <amp_math.h> a všechny jsou deklarovány pomocí restrict(amp). Funkce v <souboru hlaviček cmath> se naimportují do obou fast_math oborů názvů i precise_math do oborů názvů. Klíčové restrict slovo se používá k rozlišení <verze cmath> a verze C++ AMP. Následující kód vypočítá logaritmus base-10 pomocí rychlé metody každé hodnoty, která je ve výpočetní doméně.

#include <amp.h>
#include <amp_math.h>
#include <iostream>
using namespace concurrency;

void MathExample() {

    double numbers[] = { 1.0, 10.0, 60.0, 100.0, 600.0, 1000.0 };
    array_view<double, 1> logs(6, numbers);

    parallel_for_each(
        logs.extent,
        [=] (index<1> idx) restrict(amp) {
            logs[idx] = concurrency::fast_math::log10(numbers[idx]);
        }
    );

    for (int i = 0; i < 6; i++) {
        std::cout << logs[i] << "\n";
    }
}

Knihovna grafiky

C++ AMP obsahuje grafickou knihovnu určenou pro akcelerované programování grafiky. Tato knihovna se používá jenom na zařízeních, která podporují nativní grafické funkce. Metody jsou v Namespace Concurrency::graphics a jsou obsaženy v <souboru hlavičky amp_graphics.h> . Klíčové součásti grafické knihovny jsou:

  • texture Třída: Pomocí třídy textury můžete vytvořit textury z paměti nebo ze souboru. Textury se podobají polím, protože obsahují data a podobají se kontejnerům ve standardní knihovně C++ s ohledem na přiřazení a kopírování konstrukce. Další informace najdete v tématu Kontejnery standardní knihovny jazyka C++. Parametry šablony pro texture třídu jsou typ elementu a pořadí. Pořadí může být 1, 2 nebo 3. Typ prvku může být jedním z krátkých typů vektorů, které jsou popsány dále v tomto článku.

  • writeonly_texture_view třída: Poskytuje přístup jen pro zápis k jakékoli texturě.

  • Short Vector Library: Definuje sadu krátkých vektorových typů délky 2, 3 a 4, které jsou založeny na int, , uint, float, doublenormě nebo unorm.

aplikace pro Univerzální platforma Windows (UPW)

Stejně jako u jiných knihoven C++ můžete v aplikacích pro UPW používat C++ AMP. Tyto články popisují, jak zahrnout kód C++ AMP do aplikací vytvořených pomocí C++, C#, Visual Basicu nebo JavaScriptu:

Vizualizér C++ AMP a concurrency

Vizualizér souběžnosti zahrnuje podporu analýzy výkonu kódu C++ AMP. Tyto články popisují tyto funkce:

Doporučení k výkonu

Moduly a dělení celých čísel bez znaménka mají výrazně lepší výkon než moduly a dělení celých čísel se znaménkem. Pokud je to možné, doporučujeme používat celá čísla bez znaménka.

Viz také

C++ AMP (C++ Accelerated Massive Parallelism)
Syntaxe výrazů lambda
Referenční dokumentace (C++ AMP)
Blog o paralelním programování v nativním kódu