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.
$ ./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
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:
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
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| Op | Sonuç | Maliyet |
|---|---|---|
subgroupAdd/Min/Max | aktif lane’lerde reduction | ~1 instr |
subgroupBallot + BitCount | “kaç lane geçti” | vote + popc |
subgroupBroadcastFirst | kanonik lane’in değeri | 1 instr |
subgroupShuffle | lane↔lane değişim | 1 instr |
subgroupInclusiveAdd | prefix toplam | log2(N) shuffle |
1 numaralı hata: divergent çağrılar
Subgroup op’ları tüm aktif lane’lerin katılımını gerektirir. Şu:
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:
uint s = subgroupAdd(val); // tüm lane'ler değerlendirir
if (lane == 0) sum = s; // bir lane yazarNe 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:
subgroupBallotFindLSBile “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.