Vulkan compute shader'larında subgroup işlemleri nasıl kullanılır

Post 62. Compute shader’ınızın “invocation başına bir thread” modelinin altında donanım gerçeği yatar: invocation’lar bir subgroup olarak lockstep çalışır (NVIDIA’da warp, AMD’de wavefront — RDNA’da 64 lane). Subgroup işlemleri lane’lerin tek bir instruction’da veri değişmesini sağlar — shared memory yok, barrier yok. 64 değerlik bir reduction tek subgroupAdd‘dir.

example.sh
$ ./app
subgroup size=64 ops={ Basic | Vote | Arithmetic | Ballot | Shuffle |
  ShuffleRelative | Clustered | Quad | Rotate | RotateClustered }
sum=2080 (want 2080)  count=22 (want 22)  bcast=1  shuffled=33
OK

Example: VulkanExamples/p62_subgroup

Desteği kontrol edin, sonra SPIR-V 1.3+ için derleyin

main.cpp
auto sg = phys.getProperties2<vk::PhysicalDeviceProperties2,
    vk::PhysicalDeviceSubgroupProperties>()
    .get<vk::PhysicalDeviceSubgroupProperties>();
// sg.subgroupSize       — subgroup başına lane sayısı (burada 64)
// sg.supportedStages    — hangi stage'ler kullanabilir
// sg.supportedOperations — hangi op sınıfları var

GLSL, GL_KHR_shader_subgroup_* uzantıları ve SPIR-V 1.3 gerektirir. shaderc’nin varsayılanı SPIR-V 1.0 hedefler — derleme requires SPIR-V 1.3 hatasıyla başarısız olur. Hedefi düzeltin:

main.cpp (shaderc options)
shaderc::CompileOptions opts;
opts.SetTargetEnvironment(shaderc_target_env_vulkan,
                          shaderc_env_version_vulkan_1_1); // → SPIR-V 1.3
auto res = cc.CompileGlslToSpv(src, shaderc_compute_shader,
                               "sg.comp", opts);

Dört op ailesi

example.txt
uint lane = gl_SubgroupInvocationID;   // benim lane index'im
uint val  = lane + 1;

uint s  = subgroupAdd(val);                 // ağaç reduce: tüm lane'ler toplamı alır
uvec4 b = subgroupBallot(lane % 3 == 0);    // eşleşen lane'lerin bitmask'i
uint bc = subgroupBroadcastFirst(val);      // en düşük aktif lane'in değeri
uint sh = subgroupShuffle(val, 32);         // başka lane'in değerini oku
uint n  = subgroupBallotBitCount(b);        // ballot'ın popcount'u
OpSonuçMaliyet
subgroupAdd/Min/Maxaktif lane’lerde reduction~1 instr
subgroupBallot + BitCount“kaç lane geçti”vote + popc
subgroupBroadcastFirstkanonik lane’in değeri1 instr
subgroupShufflelane↔lane değişim1 instr
subgroupInclusiveAddprefix toplamlog2(N) shuffle

1 numaralı hata: divergent çağrılar

Subgroup op’ları tüm aktif lane’lerin katılımını gerektirir. Şu:

example.txt
if (lane == 0) {               // YANLIŞ — subgroup'ı 1 lane'e daraltır
    out = subgroupAdd(val);    // sadece lane0'ın değerini döndürür!
}

2080 yerine 1 döndürür — ve bu bir derleme hatası değil, sessiz yanlışlıktır. Doğru desen: tüm lane’lerde değerlendirip sadece store‘u kapıdan geçirin:

example.txt
uint s = subgroupAdd(val);     // tüm lane'ler değerlendirir
if (lane == 0) sum = s;        // bir lane yazar

Ne zaman kullanılır

  • Reduction’lar: shared-memory ping-pong olmadan workgroup üzerinde sum/max/min — ~6 instruction’da 64 değer.
  • Ballot destekli compaction: subgroupBallotFindLSB ile “sonraki işlenmemiş iş öğesi” — post 61’in indirect draw’ları ile mükemmel uyum.
  • Warp genelinde atomic’ler: subgroupElect + subgroup başına bir atomic.
  • Basit bir barrier()+shared[] daha açıksa uğraşmayın — subgroup op’ları zaten lane-paralel olduğunuzda parlar.

Check out similar posts by category: C++ Vulkan