Sdílet prostřednictvím


Návod: Ladění aplikace C++ AMP

Toto téma ukazuje, jak ladit aplikaci, která používá C++ Accelerated Massive Parallelism (C++ AMP) za účelem využití grafického procesoru (GPU).Používá program pro snížení paralelismu, který provede souhrn velkých polí obsahujících celá čísla.Tento návod ilustruje následující úkoly:

  • Spouštění ladicího nástroje GPU.

  • Kontrolování vláken GPU v okně GPU vláken.

  • Použití Okna paralelních zásobníků pro sledování zásobníků volání více vláken GPU najednou.

  • Použití Okna paralelních zásobníků pro kontrolu jednoho výrazu napříč více vlákny ve stejný okamžik.

  • Označování, zmrazení, rozmrazování a seskupování GPU vláken.

  • Spouštění všech vláken dlaždice na určité místo provádění kódu.

Požadavky

Před zahájením tohoto postupu:

[!POZNÁMKA]

Na vašem počítači se můžou v následujících pokynech zobrazovat jiné názvy nebo umístění některých prvků uživatelského rozhraní Visual Studia. Tyto prvky jsou určeny edicí sady Visual Studio a použitým nastavením. Další informace najdete v tématu Přizpůsobení nastavení pro vývoj v sadě Visual Studio.

Vytvořit ukázkový projekt

  1. Spusťte aplikaci Visual Studio.

  2. Na panelu nabídky vyberte možnosti Soubor, Nový, Projekt.

  3. Pod záložkou Nainstalováno v podokně šablony vyberte položku Visual C++.

  4. Zvolte Win32 konzolová aplikace, zadejte AMPMapReduce do poli Název a následně klikněte na tlačítko OK.

  5. Klikněte na tlačítko Další.

  6. Nezatrhávejte políčko Předkompilovaná hlavička a následně klikněte na tlačítko Dokončit.

  7. V Průzkumníku řešení odstraňte z projektu stdafx.h, targetver.h a stdafx.cpp.

  8. Otevřete AMPMapReduce.cpp a jeho obsah nahraďte následujícím kódem.

    // AMPMapReduce.cpp defines the entry point for the program.
    // The program performs a parallel-sum reduction that computes the sum of an array of integers. 
    
    #include <stdio.h>
    #include <tchar.h>
    #include <amp.h>
    
    const int BLOCK_DIM = 32;
    
    using namespace concurrency;
    
    void sum_kernel_tiled(tiled_index<BLOCK_DIM> t_idx, array<int, 1> &A, int stride_size) restrict(amp)
    {
        tile_static int localA[BLOCK_DIM];
    
        index<1> globalIdx = t_idx.global * stride_size;
        index<1> localIdx = t_idx.local;
    
        localA[localIdx[0]] =  A[globalIdx];
    
        t_idx.barrier.wait();
    
        // Aggregate all elements in one tile into the first element.
        for (int i = BLOCK_DIM / 2; i > 0; i /= 2) 
        {
            if (localIdx[0] < i) 
            {
    
                localA[localIdx[0]] += localA[localIdx[0] + i];
            }
    
            t_idx.barrier.wait();
        }
    
        if (localIdx[0] == 0)
        {
            A[globalIdx] = localA[0];
        }
    }
    
    int size_after_padding(int n)
    {
        // The extent might have to be slightly bigger than num_stride to 
        // be evenly divisible by BLOCK_DIM. You can do this by padding with zeros.
        // The calculation to do this is BLOCK_DIM * ceil(n / BLOCK_DIM)
        return ((n - 1) / BLOCK_DIM + 1) * BLOCK_DIM;
    }
    
    int reduction_sum_gpu_kernel(array<int, 1> input) 
    {
        int len = input.extent[0];
    
        //Tree-based reduction control that uses the CPU.
        for (int stride_size = 1; stride_size < len; stride_size *= BLOCK_DIM) 
        {
            // Number of useful values in the array, given the current
            // stride size.
            int num_strides = len / stride_size;  
    
            extent<1> e(size_after_padding(num_strides));
    
            // The sum kernel that uses the GPU.
            parallel_for_each(extent<1>(e).tile<BLOCK_DIM>(), [&input, stride_size] (tiled_index<BLOCK_DIM> idx) restrict(amp)
            {
                sum_kernel_tiled(idx, input, stride_size);
            });
        }
    
        array_view<int, 1> output = input.section(extent<1>(1));
        return output[0];
    }
    
    int cpu_sum(const std::vector<int> &arr) {
        int sum = 0;
        for (size_t i = 0; i < arr.size(); i++) {
            sum += arr[i];
        }
        return sum;
    }
    
    std::vector<int> rand_vector(unsigned int size) {
        srand(2011);
    
        std::vector<int> vec(size);
        for (size_t i = 0; i < size; i++) {
            vec[i] = rand();
        }
        return vec;
    }
    
    array<int, 1> vector_to_array(const std::vector<int> &vec) {
        array<int, 1> arr(vec.size());
        copy(vec.begin(), vec.end(), arr);
        return arr;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        std::vector<int> vec = rand_vector(10000);
        array<int, 1> arr = vector_to_array(vec);
    
        int expected = cpu_sum(vec);
        int actual = reduction_sum_gpu_kernel(arr);
    
        bool passed = (expected == actual);
        if (!passed) {
            printf("Actual (GPU): %d, Expected (CPU): %d", actual, expected);
        }
        printf("sum: %s\n", passed ? "Passed!" : "Failed!"); 
    
        getchar();
    
        return 0;
    }
    
  9. V panelu nabídek vyberte položku Soubor, Uložit vše.

  10. V Průzkumníku řešení otevřete místní nabídku pro AMPMapReduce a pak zvolte Vlastnosti.

  11. V dialogovém okně Stránky vlastností pod Vlastnosti nastavení zvolte C/C++, Předkompilované hlavičky.

  12. Pro vlastnost Předkompilovaná hlavička zvolte Nepoužívat předkompilované hlavičky a následně klikněte na tlačítko OK.

  13. V panelu nabídek zvolte Sestavit, Sestavit řešení.

Ladění kódu procesoru

V tomto postupu použijete místní ladicí program systému Windows pro ověření správnost kódu procesoru v rámci této aplikace.Část kódu procesoru v rámci této aplikace, která je obzvláště zajímavá, je for smyčka ve funkci reduction_sum_gpu_kernel.Ovládá snížení paralelismu založené na stromové struktuře, která je spouštěna na GPU.

Chcete-li ladit kód procesoru

  1. V Průzkumníku řešení otevřete místní nabídku pro AMPMapReduce a pak zvolte Vlastnosti.

  2. V dialogovém okně Stránky vlastností pod Vlastnosti nastavení zvolte Ladění.Ověřte, zda je vybrán Místní ladicí nástroj systému Windows v seznamu Ladící nástroj pro spuštění.

  3. Vraťte se k Editoru kódu.

  4. Body přerušení můžete nastavovat na řádcích kódu v následujícím obrázku (přibližně 67 řádků řádek 70).

    Zarážky procesoru

    Zarážky procesoru

  5. V panelu nabídek zvolte Ladit, Spustit ladění.

  6. V okně Lokální proměnné sledujte hodnotu stride_size, dokud nebude dosažen breakpoint na řádku 70.

  7. V panelu nabídek zvolte položku Ladit, Zastavit ladění.

Ladění kódu GPU

Tato část popisuje ladění kódu GPU, což je kód obsažený v rámci funkce sum_kernel_tiled.Kód GPU paralelně vypočítává celočíselný součet pro každý "blok".

Chcete-li ladit kód GPU

  1. V Průzkumníku řešení otevřete místní nabídku pro AMPMapReduce a pak zvolte Vlastnosti.

  2. V dialogovém okně Stránky vlastností pod Vlastnosti nastavení zvolte Ladění.

  3. V seznamu Ladící nástroj pro spuštění zvolte Místní ladicí nástroj systému Windows.

  4. V seznamu Typ ladícího nástroje zvolte Pouze GPU.

  5. Klikněte na tlačítko OK.

  6. Nastavte bod přerušení na řádku 30, jak je znázorněno na následujícím obrázku.

    Body přerušení GPU

    GPU zarážky

  7. V panelu nabídek zvolte Ladit, Spustit ladění.Body přerušení v kódu procesoru na řádcích 67 a 70 řádky nebudou provedeny během ladění GPU, protože jsou tyto řádky kódu prováděny na procesoru.

Použití okna vláken GPU

  1. Chcete-li otevřít okno vláken GPU v panelu nabídek, zvolte položku Ladit, Okna, Vlákna GPU.

    Můžete zkontrolovat stav vláken GPU v okně vláken GPU, které se zobrazí.

  2. Ukotvěte okno vláken GPU v dolní části nástroje Visual Studio.Zvolte přepínací tlačítko Rozbalit vlákno pro zobrazení dlaždice a textových polí vlákna.Okno vláken GPU zobrazuje celkový počet aktivních a blokovaných vláken GPU, jak je znázorněno na následujícím obrázku.

    Okno vláken GPU

    Vlákna GPU okno s 4 aktivní vlákna

    Pro tento výpočet je alokováno 313 dlaždic.Každá dlaždice obsahuje 32 vláken.Protože k místnímu ladění GPU dochází v rámci softwarového emulátoru, existují čtyři aktivní vlákna GPU.Čtyři vlákna provádějí instrukce současně a následně se společně přesouvají na další instrukci.

    V okno GPU vlákna jsou čtyři GPU podprocesů aktivní a 28 GPU podprocesů zablokovat na tile_barrier::wait příkaz definovat na o řádku 21 (t_idx.barrier.wait();).Všech 32 vláken náleží první dlaždici tile[0].Šipka ukazuje na řádek, který obsahuje aktuální vlákno.Pro přepnutí na jiné vlákno použijte jednu z následujících metod:

    • V řádku, na který se má přepnout vlákno v rámci okna vláken GPU, otevřete místní nabídku a zvolte Přepnout na vlákno.Pokud řádek představuje více než jedno vlákno, přepnete se na první vlákno podle souřadnic vlákna.

    • Zadejte hodnoty dlaždice a vlákna do odpovídajících textových polí a následně klikněte na tlačítko Přepnout vlákno.

    Okno zásobníku volání zobrazí zásobník volání aktuálního vlákna GPU.

Použití okna paralelních zásobníků

  1. Chcete-li otevřít okno paralelních zásobníků, zvolte v panelu nabídek položku Ladit, Okna, Paralelní zásobníky.

    Okno paralelních zásobníků můžete použít pro současnou kontrolu více rámců zásobníku pro více vláken GPU.

  2. Ukotvěte okno paralelních zásobníků v dolní části nástroje Visual Studio.

  3. Ujistěte se, že je v horním rohu seznamu vybraná položka Vlákna.Na následujícím obrázku zobrazuje okno paralelních zásobníků pohled zaměřený na volání zásobníku vláken GPU, které jste mohli vidět v okně GPU vláken.

    Okno Paralelní zásobníky

    Okno Paralelní zásobníky s 4 aktivní vlákna

    32 vláken přešlo z _kernel_stub do výrazu lambda v rámci volání funkce parallel_for_each a následně do funkce sum_kernel_tiled, kde dochází ke snižování paralelismu. 28 z 32 vláken postoupilo do příkazu tile_barrier::wait a zůstalo zablokováno na řádku 22, zatímco ostatní 4 vlákna zůstala v rámci funkce sum_kernel_tiled aktivní na řádku 30.

    Můžete zkontrolovat vlastnosti vlákna GPU, které jsou k dispozici v okně vláken GPU v rozšíření DataTip okna paralelních zásobníků.Chcete-li provést tuto akci, ponechejte ukazatel myši na rámci zásobníku sum_kernel_tiled.Následující obrázek zobrazuje DataTip.

    DataTip vlákna GPU

    Popis dat pro okno Paralelní zásobníky

    Další informace o okně paralelních zásobníků získáte na Použití okna Paralelní zásobníky.

Použití okna sledování paralelismu

  1. Chcete-li otevřít okno sledování paralelismu v panelu nabídek, zvolte položku Ladit, Okna, Sledování paralelismu, Sledování paralelismu 1.

    Okno sledování paralelismu můžete použít pro kontrolu hodnot výrazu napříč více vlákny.

  2. Ukotvěte okno sledování paralelismu 1 k dolní části nástroje Visual Studio.V tabulce existuje 32 řádků okna sledování paralelismu.Každý z nich odpovídá vláknu GPU, které se objevilo v okně vláken GPU a v okně paralelních zásobníků.Nyní můžete zadat výrazy, jejichž hodnoty chcete kontrolovat napříč všemi 32 vlákny GPU.

  3. Zvolte záhlaví sloupce Přidat sledování, zadejte localIdx a následně stiskněte klávesu Enter.

  4. Znovu zvolte záhlaví sloupce Přidat sledování, zadejte globalIdxa následně stiskněte klávesu Enter.

  5. Znovu zvolte záhlaví sloupce Přidat sledování, zadejte localA[localIdx[0]]a následně stiskněte klávesu Enter.

    Zadaný výraz můžete řadit výběrem jeho odpovídajícího záhlaví sloupce.

    Vyberte záhlaví sloupce localA [localIdx [0] pro seřazení sloupce.Následující obrázek zobrazuje výsledek řazení podle localA[localIdx [0].

    Výsledky řazení

    Paralelní okno kukátka s seřazených výsledky

    Obsah okna sledování paralelismu můžete exportovat do aplikace Excel výběrem tlačítka Excel a následným zvolením možnosti Otevřít v aplikaci Excel.Pokud máte na počítače, na kterém vyvíjíte nainstalovanou aplikaci Excel, otevře se list aplikace Excel s obsahem.

  6. V pravém horním rohu okna sledování paralelismu se nachází ovládací prvek filtru, který můžete použít k filtrování obsahu použitím logických výrazů.ENTER localA [localIdx [0] >20000 v textu ovládacího prvku filtr pole a pak zvolte klávesu Enter.

    Okno nyní obsahuje pouze vlákna, u nichž je hodnota localA[localIdx[0]] vyšší než 20000.Obsah je nadále seřazen podle sloupce localA[localIdx[0]], který je představuje dříve nastavené řazení.

Označování vláken GPU

Jednotlivá vlákna GPU můžete označit v okně vláken GPU, okně sledování paralelismu, nebo můžete označit DataTip v okně paralelních zásobníků.Pokud řádek v okně vláken GPU obsahuje více, než jedno vlákno, označení tohoto řádku označí všechna vlákna, která jsou obsažená v tomto řádku.

Označení vláken GPU příznakem

  1. Zvolte záhlaví sloupce [podproces] v okně sledování paralelismu 1 pro seřazení podle indexu dlaždice a indexu vlákna.

  2. V panelu nabídek zvolte Ladit, Pokračovat, což způsobí, že čtyři vlákna, která byla aktivní postoupí k další překážce (definováno na řádku 32 AMPMapReduce.cpp).

  3. Zvolte symbol označení příznakem na levé straně řádku, který obsahuje čtyři vlákna, která jsou nyní aktivní.

    Následující obrázek zobrazuje čtyři aktivní vlákna označená příznakem v okně vláken GPU.

    Aktivní vlákna v okně vláken GPU

    Vlákna GPU okno s příznakem vlákna

    Okno sledování paralelismu a DataTip okna paralelních zásobníků ukazují vlákna označená příznakem.

  4. Pokud se chcete zaměřit na čtyři vlákna, která jste označili příznakem, můžete zobrazit v rámci oken vláken GPU, oken sledování paralelismu a oken sledování zásobníků pouze vlákna označená příznakem.

    Klikněte na tlačítko Zobrazit pouze s příznakem na libovolné okno nebo na panel nástrojů Umístění ladění.Následující obrázek zobrazuje tlačítko Zobrazit pouze s příznakem na panelu nástrojů Umístění ladění.

    Zobrazit tlačítko Pouze s označením příznakem

    Ladění umístění nástrojů s ikonou zobrazit pouze s příznakem

    Okna vláken GPU, sledování paralelismu a paralelních zásobníků zobrazují pouze vlákna označená příznakem.

Zmrazení a uvolňování vláken GPU

Můžete zmrazit (pozastavit) a uvolnit (pokračovat) vlákna GPU buď z okna vláken GPU nebo z okna sledování paralelismu.Můžete zmrazit a uvolnit vlákna CPU stejným způsobem; informace naleznete v tématu Postupy: Použití okna vláken.

Zmrazení a uvolnění vláken GPU

  1. Klikněte na tlačítko Zobrazit pouze označené příznakem pro zobrazení všech vláken.

  2. V panelu nabídek zvolte Ladit, Pokračovat.

  3. Otevřete místní nabídku aktivního řádku a následně zvolte Zmrazit.

    Následující obrázek okna vláken GPU zobrazuje zmrazení všech vláken

    Zmrazená vlákna v okně vláken GPU

    Vlákna GPU oken zobrazení zmrazených vlákna

    Podobně, okno sledování paralelismu zobrazuje zmražení všech čtyř vláken.

  4. V panelu nabídek zvolte Ladit, Pokračovat pro povolení dalším vláknům GPU v postupu přes překážku na řádku 22 a pro dosažení bodu přerušení na řádku 30.Okno vláken GPU zobrazuje, že čtyři dříve zmrazená vlákna zůstávají zmrazená a v aktivním stavu.

  5. V panelu nabídek zvolte Ladit, Pokračovat.

  6. Z okna sledování paralelismu můžete uvolnit jedno nebo více vláken GPU.

Seskupení vláken GPU

  1. V místní nabídce jednoho z vláken v okně Vlákna GPU zvolte Seskupit, Adresa.

    Vlákna v okně vláken GPU jsou seskupena podle adresy.Adresa odpovídá instrukci v rozložení, kde jsou umístěny všechny skupiny vláken. 24 vláken se nachází na řádku 22, kde se provádí tile_barrier::wait – metoda. 12 vláken se nachází na instrukci překážky na řádku 32.Čtyři z těchto vláken jsou označeny příznakem.Osm vláken se nachází na bodu přerušení na řádku 30.Čtyři z těchto vláken jsou zmrazené.Následující obrázek zobrazuje seskupená vlákna v okně vláken GPU.

    Seskupená vlákna v okně vláken GPU

    Vlákna GPU okno s vlákny seskupených podle adresy

  2. Můžete provést také operaci Seskupit podle otevřením místní nabídky datové mřížky okna sledování paralelismu zvolením položky Seskupit podle a následným výběrem položky nabídky, která odpovídá tomu, jak chcete vlákna seskupit.

Spuštění všech vláken na určitém místě v kódu

Lze spustit všechna vlákna v dané dlaždici do řádku, na který ukazuje kurzor, pomocí Přehrát aktuální dlaždici po kurzor.

Spuštění všech vláken na umístění označené kurzorem

  1. V místní nabídce zmrazených vláken zvolte Uvolnit.

  2. V editoru kódu umístěte kurzor na řádek 30.

  3. V místní nabídce editoru kódu zvolte Přehrát aktuální dlaždici po kurzor.

    24 vláken, které byla dříve blokována na překážce na řádku 21, pokročila na řádek 32.Je to znázorněno v okně Vlákna GPU.

Viz také

Úkoly

Postupy: Použití okna vláken GPU

Postupy: Použití okna paralelního sledování

Koncepty

Přehled produktu C++ AMP

Další zdroje

Ladění kódu GPU

Analýza kódu jazyka C++ AMP s Concurrency Visualizer.