Aracılığıyla paylaş


İzlenecek yol: C++ AMP uygulamasında hata ayıklama

Bu makalede, grafik işleme biriminden (GPU) yararlanmak için C++ Hızlandırılmış Yüksek Paralellik (C++ AMP) kullanan bir uygulamada hata ayıklama işlemi gösterilmektedir. Büyük bir tamsayı dizisini toplayan paralel azaltma programı kullanır. Bu izlenecek yol aşağıdaki görevleri gösterir:

  • GPU hata ayıklayıcısını başlatma.
  • GPU İş Parçacıkları penceresinde GPU iş parçacıklarını inceleme.
  • Birden çok GPU iş parçacığının çağrı yığınlarını aynı anda gözlemlemek için Paralel Yığınlar penceresini kullanma.
  • Birden çok iş parçacığındaki tek bir ifadenin değerlerini aynı anda incelemek için Paralel İzleme penceresini kullanma.
  • GPU iş parçacıklarını bayraklama, dondurma, çözme ve gruplandırma.
  • Bir kutucuğun tüm iş parçacıklarını koddaki belirli bir konuma yürütme.

Ön koşullar

Bu izlenecek yolu başlatmadan önce:

Dekont

C++ AMP üst bilgileri Visual Studio 2022 sürüm 17.0'dan itibaren kullanım dışı bırakılmıştır. Tüm AMP üst bilgileri dahil olmak derleme hataları oluşturur. Uyarıları susturmak için AMP üst bilgilerini eklemeden önce tanımlayın _SILENCE_AMP_DEPRECATION_WARNINGS .

  • C++ AMP'ye Genel Bakış'a bakın.
  • Metin düzenleyicisinde satır numaralarının görüntülendiğinden emin olun. Daha fazla bilgi için bkz . Nasıl yapılır: Düzenleyicide satır numaralarını görüntüleme.
  • Yazılım öykünücüsnde hata ayıklamayı desteklemek için en az Windows 8 veya Windows Server 2012 çalıştırdığınızdan emin olun.

Dekont

Bilgisayarınız, aşağıdaki yönergelerde yer alan Visual Studio kullanıcı arabirimi öğelerinden bazıları için farklı adlar veya konumlar gösterebilir. Sahip olduğunuz Visual Studio sürümü ve kullandığınız ayarlar bu öğeleri belirler. Daha fazla bilgi için bkz. IDE’yi kişiselleştirme.

Örnek projeyi oluşturmak için

Proje oluşturma yönergeleri, hangi Visual Studio sürümünü kullandığınıza bağlı olarak değişir. Bu sayfadaki içindekiler tablosunun üzerinde doğru belge sürümünü seçtiğinizden emin olun.

Visual Studio'da örnek proje oluşturmak için

  1. Menü çubuğunda Dosya Yeni Proje'yi seçerek >Yeni>Proje Oluştur iletişim kutusunu açın.

  2. İletişim kutusunun üst kısmında Dil'i C++ olarak, Platform'ı Windows olarak ve Proje türü'nü Konsol olarak ayarlayın.

  3. Filtrelenen proje türleri listesinden Konsol Uygulaması'nı ve ardından İleri'yi seçin. Sonraki sayfada, proje için bir ad belirtmek için Ad kutusuna girin AMPMapReduceve farklı bir ad istiyorsanız proje konumunu belirtin.

    Screenshot showing the Create a new project dialog with the Console App template selected.

  4. İstemci projesini oluşturmak için Oluştur düğmesini seçin.

Visual Studio 2017 veya Visual Studio 2015'te örnek proje oluşturmak için

  1. Visual Studio’yu çalıştırın.

  2. Menü çubuğunda Dosya Yeni Proje'yi> seçin.>

  3. Şablonlar bölmesinde yüklü'nin altında Visual C++ öğesini seçin.

  4. Win32 Konsol Uygulaması'nı seçin, Ad kutusuna yazın AMPMapReduce ve ardından Tamam düğmesini seçin.

  5. İleri düğmesini seçin.

  6. Önceden derlenmiş üst bilgi onay kutusunu temizleyin ve son düğmesini seçin.

  7. Çözüm Gezgini'da projeden stdafx.h, targetver.h ve stdafx.cpp dosyasını silin.

İleri:

  1. AMPMapReduce.cpp dosyasını açın ve içeriğini aşağıdaki kodla değiştirin.

    // 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;
    }
    
  2. Menü çubuğunda Dosya>Tümünü Kaydet'i seçin.

  3. Çözüm Gezgini,AMPMapReduce kısayol menüsünü açın ve özellikler'i seçin.

  4. Özellik Sayfaları iletişim kutusundaki Yapılandırma Özellikleri'nin altında C/C++>Önceden Derlenmiş Üst Bilgiler'i seçin.

  5. Önceden Derlenmiş Üst Bilgi özelliği için, Önceden Derlenmiş Üst Bilgileri Kullanma'yı seçin ve ardından Tamam düğmesini seçin.

  6. Menü çubuğunda Derleme Çözümü Oluştur'u>seçin.

CPU Kodunda Hata Ayıklama

Bu yordamda, bu uygulamadaki CPU kodunun doğru olduğundan emin olmak için Yerel Windows Hata Ayıklayıcısı'nı kullanacaksınız. Bu uygulamadaki CPU kodunun özellikle ilginç olan bölümü işlevdeki reduction_sum_gpu_kernel döngüdürfor. GPU üzerinde çalıştırılacak ağaç tabanlı paralel azaltmayı denetler.

CPU kodunda hata ayıklamak için

  1. Çözüm Gezgini,AMPMapReduce kısayol menüsünü açın ve özellikler'i seçin.

  2. Özellik Sayfaları iletişim kutusundaki Yapılandırma Özellikleri'nin altında Hata Ayıklama'yı seçin. Başlatilecek Hata Ayıklayıcı listesinde Yerel Windows Hata Ayıklayıcısı'nın seçili olduğunu doğrulayın.

  3. Kod Düzenleyicisi'ne dönün.

  4. Aşağıdaki çizimde gösterilen kod satırlarında kesme noktaları ayarlayın (yaklaşık satır 67 satır 70).

    CPU breakpoints marked next to lines of code in the editor.
    CPU kesme noktaları

  5. Menü çubuğunda Hata AyıklamaYı>Başlat Hata Ayıklama'yı seçin.

  6. Yerel Ayarlar penceresinde, 70. satırdaki kesme noktasına ulaşılana kadar değerini stride_size gözlemleyin.

  7. Menü çubuğunda Hata AyıklamaYı>Durdur Hata Ayıklama'yı seçin.

GPU Kodunda Hata Ayıklama

Bu bölümde, işlevde yer alan kod olan GPU kodunda hata ayıklama işlemi sum_kernel_tiled gösterilmektedir. GPU kodu, her "blok" için tamsayıların toplamını paralel olarak hesaplar.

GPU kodunda hata ayıklamak için

  1. Çözüm Gezgini,AMPMapReduce kısayol menüsünü açın ve özellikler'i seçin.

  2. Özellik Sayfaları iletişim kutusundaki Yapılandırma Özellikleri'nin altında Hata Ayıklama'yı seçin.

  3. Başlatacak Hata Ayıklayıcı listesinde Yerel Windows Hata Ayıklayıcısı'nı seçin.

  4. Hata Ayıklayıcı Türü listesinde Otomatik'in seçili olduğunu doğrulayın.

    Otomatik varsayılan değerdir. Windows 10'un önceki sürümlerinde, Otomatik yerine Yalnızca GPU gerekli değerdir.

  5. Tamam düğmesini seçin.

  6. Aşağıdaki çizimde gösterildiği gibi 30. satırda bir kesme noktası ayarlayın.

    GPU breakpoints marked next to a line of code in the editor.
    GPU kesme noktası

  7. Menü çubuğunda Hata AyıklamaYı>Başlat Hata Ayıklama'yı seçin. CPU kodundaki 67 ve 70. satırlardaki kesme noktaları GPU hata ayıklaması sırasında yürütülemez çünkü bu kod satırları CPU üzerinde çalıştırılır.

GPU İş Parçacıkları penceresini kullanmak için

  1. GPU İş Parçacıkları penceresini açmak için menü çubuğunda Windows>GPU İş Parçacıklarında Hata Ayıklama'yı>seçin.

    Görüntülenen GPU İş Parçacıkları penceresinde GPU iş parçacıklarının durumunu inceleyebilirsiniz.

  2. GPU İş Parçacıkları penceresini Visual Studio'nun altına sabitleyin. Kutucuğu ve iş parçacığı metin kutularını görüntülemek için İş Parçacığı Anahtarını Genişlet düğmesini seçin. GPU İş Parçacıkları penceresi, aşağıdaki çizimde gösterildiği gibi toplam etkin ve engellenen GPU iş parçacığı sayısını gösterir.

    GPU Threads window with 4 active threads.
    GPU İş Parçacıkları penceresi

    Bu hesaplama için 313 kutucuk ayrılır. Her kutucuk 32 iş parçacığı içerir. Yerel GPU hata ayıklaması bir yazılım öykünücüsnde gerçekleştiği için dört etkin GPU iş parçacığı vardır. Dört iş parçacığı yönergeleri aynı anda yürütür ve bir sonraki yönergeye birlikte devam eder.

    GPU İş Parçacıkları penceresinde, yaklaşık 21t_idx.barrier.wait(); . satırda tanımlanan tile_barrier::wait deyiminde engellenen dört GPU iş parçacığı ve 28 GPU iş parçacığı vardır. 32 GPU iş parçacığının tümü ilk kutucuğa aittir: tile[0]. Geçerli iş parçacığını içeren satırı gösteren bir ok. Farklı bir iş parçacığına geçmek için aşağıdaki yöntemlerden birini kullanın:

    • GPU İş Parçacıkları penceresinde iş parçacığının geçişine yönelik satırda, kısayol menüsünü açın ve İş Parçacığına Geç'i seçin. Satır birden fazla iş parçacığını temsil ederse, iş parçacığı koordinatlarına göre ilk iş parçacığına geçersiniz.

    • İlgili metin kutularına yazışmanın kutucuk ve iş parçacığı değerlerini girin ve ardından İş Parçacığını Değiştir düğmesini seçin.

    Çağrı Yığını penceresi, geçerli GPU iş parçacığının çağrı yığınını görüntüler.

Paralel Yığınlar penceresini kullanmak için

  1. Paralel Yığınlar penceresini açmak için menü çubuğunda Windows>Paralel Yığınlarında Hata Ayıklama'yı>seçin.

    Birden çok GPU iş parçacığının yığın çerçevelerini aynı anda incelemek için Paralel Yığınlar penceresini kullanabilirsiniz.

  2. Paralel Yığınlar penceresini Visual Studio'nun altına sabitleyin.

  3. Sol üst köşedeki listede İş Parçacıkları'nın seçili olduğundan emin olun. Aşağıdaki çizimde Paralel Yığınlar penceresi, GPU İş Parçacıkları penceresinde gördüğünüz GPU iş parçacıklarının çağrı yığını odaklı görünümünü gösterir.

    Parallel Stacks window with 4 active threads.
    Paralel Yığınlar penceresi

    32 iş parçacığı, _kernel_stub işlev çağrısında parallel_for_each lambda deyimine ve ardından paralel azaltmanın sum_kernel_tiled gerçekleştiği işleve gitti. 32 iş parçacığından 28'i deyimine tile_barrier::wait ilerlemiş ve 22. satırda engellenmiş durumda kalırken, diğer dört iş parçacığı 30. satırdaki işlevde sum_kernel_tiled etkin kalır.

    GPU iş parçacığının özelliklerini inceleyebilirsiniz. Bunlar, Paralel Yığınlar penceresinin zengin Veri İpucu'ndaki GPU İş Parçacıkları penceresinde kullanılabilir. Bunları görmek için işaretçiyi öğesinin yığın çerçevesinin sum_kernel_tiledüzerine getirin. Aşağıdaki çizimde Veri İpucu gösterilmektedir.

    DataTip for Parallel Stacks window.
    GPU iş parçacığı Veri İpucu

    Paralel Yığınlar penceresi hakkında daha fazla bilgi için bkz. Paralel Yığınlar Penceresini Kullanma.

Paralel İzleme penceresini kullanmak için

  1. Parallel Watch penceresini açmak için menü çubuğunda Windows>Parallel Watch Parallel Watch>1'de Hata Ayıklama'yı>seçin.

    Birden çok iş parçacığında bir ifadenin değerlerini incelemek için Paralel İzleme penceresini kullanabilirsiniz.

  2. Parallel Watch 1 penceresini Visual Studio'nun altına sabitleyin. Paralel İzleme penceresinin tablosunda 32 satır vardır. Her bir iş parçacığı hem GPU İş Parçacıkları penceresinde hem de Paralel Yığınlar penceresinde görünen bir GPU iş parçacığına karşılık gelir. Artık 32 GPU iş parçacığında değerlerini incelemek istediğiniz ifadeleri girebilirsiniz.

  3. gözcü sütunu ekle üst bilgisini seçin, yazın localIdxve enter tuşunu seçin.

  4. Saat Ekle sütun üst bilgisini yeniden seçin, yazın globalIdxve enter tuşunu seçin.

  5. Saat Ekle sütun üst bilgisini yeniden seçin, yazın localA[localIdx[0]]ve enter tuşunu seçin.

    Belirtilen bir ifadeye göre, ilgili sütun üst bilgisini seçerek sıralama yapabilirsiniz.

    Sütunu sıralamak için localA[localIdx[0]] sütun başlığını seçin. Aşağıdaki çizimde localA[localIdx[0]] ölçütüne göre sıralamanın sonuçları gösterilmektedir.

    Parallel Watch window with sorted results.
    Sıralama sonuçları

    Paralel İzleme penceresindeki içeriği Excel'e aktarmak için Excel düğmesini ve ardından Excel'de Aç'ı seçebilirsiniz. Geliştirme bilgisayarınızda Excel yüklüyse, düğme içeriği içeren bir Excel çalışma sayfası açar.

  6. Paralel İzleme penceresinin sağ üst köşesinde, Boole ifadelerini kullanarak içeriği filtrelemek için kullanabileceğiniz bir filtre denetimi vardır. Filtre denetimi metin kutusuna girin localA[localIdx[0]] > 20000 ve Enter tuşunu seçin.

    Pencere artık yalnızca değerin 20000'den büyük olduğu localA[localIdx[0]] iş parçacıklarını içerir. İçerik yine de daha önce seçtiğiniz sıralama eylemi olan sütuna göre localA[localIdx[0]] sıralanır.

GPU İş Parçacıklarını Etiketleme

Belirli GPU iş parçacıklarını GPU İş Parçacıkları penceresinde, Paralel İzleme penceresinde veya Paralel Yığınlar penceresindeki Veri İpucu'nda işaretleyerek işaretleyebilirsiniz. GPU İş Parçacıkları penceresindeki bir satırda birden fazla iş parçacığı varsa, bu satırın bayrakla işaretlenmesi satırda yer alan tüm iş parçacıklarına bayrak ekler.

GPU iş parçacıklarına bayrak eklemek için

  1. Kutucuk dizinine ve iş parçacığı dizinine göre sıralamak için Parallel Watch 1 penceresinde [thread] sütun başlığını seçin.

  2. Menü çubuğunda Hata Ayıklama Devamı'nı> seçin; bu da etkin olan dört iş parçacığının bir sonraki engele ilerlemesine neden olur (AMPMapReduce.cpp dosyasının 32. satırında tanımlanır).

  3. Şimdi etkin olan dört iş parçacığını içeren satırın sol tarafındaki bayrak simgesini seçin.

    Aşağıdaki çizimde GPU İş Parçacıkları penceresindeki dört etkin bayrak eklenmiş iş parçacığı gösterilmektedir .

    GPU Threads window with flagged threads.
    GPU İş Parçacıkları penceresindeki etkin iş parçacıkları

    Paralel İzleme penceresi ve Paralel Yığınlar penceresinin Veri İpucu, bayrak eklenmiş iş parçacıklarını gösterir.

  4. Bayrak eklediğiniz dört iş parçacığına odaklanmak istiyorsanız, yalnızca bayrak eklenmiş iş parçacıklarını göstermeyi seçebilirsiniz. GPU İş Parçacıkları, Paralel İzleme ve Paralel Yığınlar pencerelerinde gördüklerinizi sınırlar.

    Pencerelerden herhangi birinde veya Hata Ayıklama Konumu araç çubuğunda Yalnızca Bayrak Eklenmişi Göster düğmesini seçin. Aşağıdaki çizimde, Hata Ayıklama Konumu araç çubuğundaki Yalnızca Bayrak eklenmişi Göster düğmesi gösterilmektedir.

    Debug Location toolbar with Show Only Flagged icon.
    Yalnızca Bayrak eklenmiş düğmeyi göster

    Artık GPU İş Parçacıkları, Paralel İzleme ve Paralel Yığınlar pencereleri yalnızca bayrak eklenmiş iş parçacıklarını görüntüler.

GPU İş Parçacıklarını Dondurma ve Çözme

GPU İş Parçacıkları penceresinden veya Paralel İzleme penceresinden GPU iş parçacıklarını dondurabilir (askıya alabilir) ve çözebilirsiniz (sürdürebilirsiniz). CPU iş parçacıklarını aynı şekilde dondurabilir ve çözebilirsiniz; bilgi için bkz . Nasıl yapılır: İş Parçacıkları Penceresini Kullanma.

GPU iş parçacıklarını dondurmak ve çözmek için

  1. Tüm iş parçacıklarını görüntülemek için Yalnızca Bayrak Eklenmişi Göster düğmesini seçin.

  2. Menü çubuğunda Devam Hatalarını Ayıkla'yı>seçin.

  3. Etkin satırın kısayol menüsünü açın ve Dondur'u seçin.

    GPU İş Parçacıkları penceresinin aşağıdaki çiziminde dört iş parçacığının da dondurulduğunu görebilirsiniz.

    GPU Threads windows showing frozen threads.
    GPU İş Parçacıkları penceresinde donmuş iş parçacıkları

    Benzer şekilde, Parallel Watch penceresi dört iş parçacığının da dondurulduğunu gösterir.

  4. Sonraki dört GPU iş parçacığının 22. satırdaki engeli aşmasına ve 30. satırdaki kesme noktasına ulaşmasına izin vermek için menü çubuğunda Hata Ayıkla>Devam'ı seçin. GPU İş Parçacıkları penceresi, önceden dondurulan dört iş parçacığının donmuş ve etkin durumda kaldığını gösterir.

  5. Menü çubuğunda Hata Ayıkla, Devam'ı seçin.

  6. Paralel İzleme penceresinden tek tek veya birden çok GPU iş parçacığını da çözebilirsiniz.

GPU iş parçacıklarını gruplandırmak için

  1. GPU İş Parçacıkları penceresindeki iş parçacıklarından birinin kısayol menüsünde Gruplandırma Ölçütü, Adres'i seçin.

    GPU İş Parçacıkları penceresindeki iş parçacıkları adrese göre gruplandırılır. Adres, her iş parçacığı grubunun bulunduğu ayrıştırma yönergesine karşılık gelir. 24 iş parçacığı, tile_barrier::wait Yönteminin yürütüldüğü 22. satırdadır. 12 iş parçacığı, 32. satırdaki bariyer için talimattadır. Bu iş parçacıklarından dördü işaretlenir. 30. satırdaki kesme noktasında sekiz iş parçacığı vardır. Bu ipliklerden dördü donduruldu. Aşağıdaki çizimde GPU İş Parçacıkları penceresinde gruplandırılmış iş parçacıkları gösterilmektedir .

    GPU Threads window with threads grouped by Address.
    GPU İş Parçacıkları penceresinde gruplandırılmış iş parçacıkları

  2. Paralel İzleme penceresinin veri kılavuzu için kısayol menüsünü açarak Gruplandırma Ölçütü işlemini de yapabilirsiniz. Gruplandır'ı seçin ve ardından iş parçacıklarını nasıl gruplandırmak istediğinize karşılık gelen menü öğesini seçin.

Koddaki Belirli Bir Konuma Tüm İş Parçacıklarını Çalıştırma

Geçerli Kutucuğu İmleç için Çalıştır'ı kullanarak belirli bir kutucuktaki tüm iş parçacıklarını imleci içeren satıra çalıştırırsınız.

İmleç tarafından işaretlenen konuma tüm iş parçacıklarını çalıştırmak için

  1. Dondurulmuş iş parçacıklarının kısayol menüsünde Çöz'e tıklayın.

  2. Kod Düzenleyicisi'nde imleci 30. satıra getirin.

  3. Kod Düzenleyicisi'nin kısayol menüsünde Geçerli Kutucuğu İmleç olarak Çalıştır'ı seçin.

    Daha önce 21. satırdaki bariyerde engellenen 24 iş parçacığı 32. satıra ilerlemiş. GPU İş Parçacıkları penceresinde gösterilir.

Ayrıca bkz.

C++ AMP'ye genel bakış
GPU kodunda hata ayıklama
Nasıl yapılır: GPU İş Parçacıkları penceresini kullanma
Nasıl yapılır: Paralel İzleme penceresini kullanma
Eşzamanlılık Görselleştiricisi ile C++ AMP kodunu çözümleme