vulkan: Tune GDN kernel, fix Intel performance (#29476)

* vulkan: tune GDN shader

* tune for Intel
This commit is contained in:
Ruben Ortlam
2026-09-29 20:44:20 +03:00
committed by GitHub
parent 83dd71f869
commit 5c200e0c8d
+20 -10
View File
@@ -3668,33 +3668,43 @@ void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
const uint32_t S_V = gdn_sizes[si];
GGML_ASSERT(is_pow2(S_V));
// Intel Xe regresses at SIMD32 for this scan; prefer a narrower subgroup.
uint32_t gdn_subgroup_size = device->subgroup_size;
if (device->vendor_id == VK_VENDOR_ID_INTEL && device->subgroup_size_control &&
device->subgroup_min_size <= 16u && device->subgroup_max_size >= 16u) {
gdn_subgroup_size = 16u;
}
uint32_t lanes_per_column;
if (S_V >= 128u && device->subgroup_clustered) {
lanes_per_column = 8u;
if (device->vendor_id == VK_VENDOR_ID_INTEL) {
// Intel Xe: full-width reduction (min rows/lane) ~10x over the COLS_PER_WG=8 rule.
lanes_per_column = std::min(gdn_subgroup_size, S_V);
} else if (S_V >= 128u && device->subgroup_clustered) {
// COLS_PER_WG=8: measured optimum (Ampere sg32->4, Vega20 sg64->8).
lanes_per_column = std::max(1u, gdn_subgroup_size / 8u);
} else {
// Use largest power-of-two that divides both S_V and subgroup_size so that
// (1) S_V % lanes_per_column == 0 and (2) S_V % (subgroup_size / lanes_per_column) == 0.
// This means we don't need extra bounds checking logic in the shader.
lanes_per_column = std::min(S_V, device->subgroup_size);
lanes_per_column = std::min(S_V, gdn_subgroup_size);
}
// gated_delta_net.comp relies on S_V % COLS_PER_WG == 0 and
// S_V % LANES_PER_COLUMN == 0 to avoid bounds checks.
while (lanes_per_column > 1u) {
const bool valid_lanes = (device->subgroup_size % lanes_per_column) == 0 &&
const bool valid_lanes = (gdn_subgroup_size % lanes_per_column) == 0 &&
(S_V % lanes_per_column) == 0;
const uint32_t cols_per_wg = valid_lanes ? device->subgroup_size / lanes_per_column : 0;
const uint32_t cols_per_wg = valid_lanes ? gdn_subgroup_size / lanes_per_column : 0;
if (valid_lanes && cols_per_wg > 0 && (S_V % cols_per_wg) == 0) {
break;
}
lanes_per_column >>= 1u;
}
GGML_ASSERT((device->subgroup_size % lanes_per_column) == 0);
GGML_ASSERT((gdn_subgroup_size % lanes_per_column) == 0);
GGML_ASSERT((S_V % lanes_per_column) == 0);
GGML_ASSERT((S_V % (device->subgroup_size / lanes_per_column)) == 0);
GGML_ASSERT((S_V % (gdn_subgroup_size / lanes_per_column)) == 0);
const bool need_partial_subgroup_reduce = lanes_per_column != 1u && lanes_per_column < device->subgroup_size;
const bool need_partial_subgroup_reduce = lanes_per_column != 1u && lanes_per_column < gdn_subgroup_size;
const bool use_clustered_reduce = device->subgroup_arithmetic && device->subgroup_clustered && need_partial_subgroup_reduce;
const bool use_subgroup_reduce = device->subgroup_arithmetic && !need_partial_subgroup_reduce;
const bool use_subgroup_ops = use_clustered_reduce || use_subgroup_reduce;
@@ -3711,13 +3721,13 @@ void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
gdn_data = (const void *)gated_delta_net_f32_shmem_data;
}
const uint32_t cols_per_wg = device->subgroup_size / lanes_per_column;
const uint32_t cols_per_wg = gdn_subgroup_size / lanes_per_column;
const std::array<uint32_t, 3> wg_denoms = {1u, 1u, cols_per_wg};
for (uint32_t kda = 0; kda < 2; kda++) {
ggml_vk_create_pipeline(device, device->pipeline_gated_delta_net[si][kda],
gdn_names[si][kda], gdn_len, gdn_data, "main", 7, sizeof(vk_op_gated_delta_net_push_constants),
wg_denoms, {S_V, kda, device->subgroup_size, lanes_per_column}, 1, true, use_subgroup_ops, device->subgroup_size);
wg_denoms, {S_V, kda, gdn_subgroup_size, lanes_per_column}, 1, true, use_subgroup_ops, gdn_subgroup_size);
}
}
}