From 0037db781ef51f4b0328733d04b9c945ebdde1b2 Mon Sep 17 00:00:00 2001 From: Redkin-Aleksey Date: Wed, 7 Oct 2026 20:47:25 +0300 Subject: [PATCH 1/2] gh-246: gate single-group launches by per-kernel work-group size limit --- src/opencl/cl_program_builder.cpp | 10 +++++++++- src/opencl/cl_program_builder.hpp | 2 ++ src/opencl/cl_reduce_by_key.hpp | 3 ++- src/opencl/cl_sort_by_key.hpp | 23 ++++++++++++++++------- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/opencl/cl_program_builder.cpp b/src/opencl/cl_program_builder.cpp index 0c17e4e13..64b53bdd2 100644 --- a/src/opencl/cl_program_builder.cpp +++ b/src/opencl/cl_program_builder.cpp @@ -62,6 +62,14 @@ namespace spla { m_source = source; return *this; } + + size_t kernel_group_limit(CLProgramBuilder& builder, CLAccelerator* acc, const char* kernel_name) { + size_t limit = 0; + cl::Kernel kernel = builder.make_kernel(kernel_name); + clGetKernelWorkGroupInfo(kernel(), acc->get_device()(), CL_KERNEL_WORK_GROUP_SIZE, sizeof(limit), &limit, nullptr); + return limit; + } + void CLProgramBuilder::acquire() { CLAccelerator* acc = get_acc_cl(); CLProgramCache* cache = acc->get_cache(); @@ -119,4 +127,4 @@ namespace spla { cache->add_program(m_program); } -}// namespace spla \ No newline at end of file +}// namespace spla diff --git a/src/opencl/cl_program_builder.hpp b/src/opencl/cl_program_builder.hpp index 2b0431e22..477857f09 100644 --- a/src/opencl/cl_program_builder.hpp +++ b/src/opencl/cl_program_builder.hpp @@ -75,6 +75,8 @@ namespace spla { std::string m_program_code; }; + size_t kernel_group_limit(CLProgramBuilder& builder, CLAccelerator* acc, const char* kernel_name); + /** * @} */ diff --git a/src/opencl/cl_reduce_by_key.hpp b/src/opencl/cl_reduce_by_key.hpp index 7aceba0af..2f60a9f85 100644 --- a/src/opencl/cl_reduce_by_key.hpp +++ b/src/opencl/cl_reduce_by_key.hpp @@ -59,7 +59,8 @@ namespace spla { const uint block_size = cl_acc->get_default_wgs(); const uint sequential_switch = 32; - const uint small_switch = cl_acc->get_max_wgs(); + const uint kernel_limit = kernel_group_limit(builder, cl_acc, "reduce_by_key_small"); + const uint small_switch = kernel_limit / cl_acc->get_wave_size() * cl_acc->get_wave_size(); if (size == 0) { reduced_size = 0; diff --git a/src/opencl/cl_sort_by_key.hpp b/src/opencl/cl_sort_by_key.hpp index 038aa1b04..ff888bb6a 100644 --- a/src/opencl/cl_sort_by_key.hpp +++ b/src/opencl/cl_sort_by_key.hpp @@ -41,10 +41,10 @@ namespace spla { template - void cl_sort_by_key_bitonic(cl::CommandQueue& queue, cl::Buffer& keys, cl::Buffer& values, uint size) { + bool cl_sort_by_key_bitonic(cl::CommandQueue& queue, cl::Buffer& keys, cl::Buffer& values, uint size) { if (size <= 1) { LOG_MSG(Status::Ok, "nothing to do"); - return; + return true; } auto* acc = get_acc_cl(); @@ -71,14 +71,20 @@ namespace spla { const uint wave_size = acc->get_wave_size(); const uint n_threads = align(std::min(size, max_treads_per_block), wave_size); + if (n_threads > kernel_group_limit(builder, acc, "bitonic_sort_local")) { + return false; + } cl::NDRange global(n_threads); cl::NDRange local(n_threads); queue.enqueueNDRangeKernel(kernel_local, cl::NDRange(), global, local); - return; + return true; } const uint n_groups = div_up(size, local_size); + if (max_treads_per_block > kernel_group_limit(builder, acc, "bitonic_sort_local")) { + return false; + } cl::NDRange step_pre_sort_global(max_treads_per_block * n_groups); cl::NDRange step_pre_sort_local(max_treads_per_block); queue.enqueueNDRangeKernel(kernel_local, cl::NDRange(), step_pre_sort_global, step_pre_sort_local); @@ -89,9 +95,13 @@ namespace spla { kernel_global.setArg(2, size); kernel_global.setArg(3, uint(local_size * 2)); + if (acc->get_max_wgs() > kernel_group_limit(builder, acc, "bitonic_sort_global")) { + return false; + } cl::NDRange step_final_global(acc->get_max_wgs()); cl::NDRange step_final_local(acc->get_max_wgs()); queue.enqueueNDRangeKernel(kernel_global, cl::NDRange(), step_final_global, step_final_local); + return true; } template @@ -180,11 +190,10 @@ namespace spla { const uint sort_switch = 2u << 14u; - if (n <= sort_switch) { - cl_sort_by_key_bitonic(queue, keys, values, n); - } else { - cl_sort_by_key_radix(queue, keys, values, n, tmp_alloc, max_key); + if (n <= sort_switch && cl_sort_by_key_bitonic(queue, keys, values, n)) { + return; } + cl_sort_by_key_radix(queue, keys, values, n, tmp_alloc, max_key); } }// namespace spla From 1e225cbd8639358b7048ee7752fdb6d761ee29f4 Mon Sep 17 00:00:00 2001 From: Redkin-Aleksey Date: Wed, 14 Oct 2026 15:10:27 +0300 Subject: [PATCH 2/2] gh-246: launch bitonic groups at per-kernel limit instead of falling back to radix --- src/opencl/cl_program_builder.cpp | 6 ++++++ src/opencl/cl_program_builder.hpp | 1 + src/opencl/cl_reduce_by_key.hpp | 3 +-- src/opencl/cl_sort_by_key.hpp | 28 +++++++++++++--------------- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/opencl/cl_program_builder.cpp b/src/opencl/cl_program_builder.cpp index 64b53bdd2..e457f429f 100644 --- a/src/opencl/cl_program_builder.cpp +++ b/src/opencl/cl_program_builder.cpp @@ -70,6 +70,12 @@ namespace spla { return limit; } + uint capped_group_size(CLProgramBuilder& builder, CLAccelerator* acc, const char* kernel_name) { + const size_t limit = kernel_group_limit(builder, acc, kernel_name); + const size_t wave = acc->get_wave_size(); + return static_cast(limit / wave * wave); + } + void CLProgramBuilder::acquire() { CLAccelerator* acc = get_acc_cl(); CLProgramCache* cache = acc->get_cache(); diff --git a/src/opencl/cl_program_builder.hpp b/src/opencl/cl_program_builder.hpp index 477857f09..76f12f0b4 100644 --- a/src/opencl/cl_program_builder.hpp +++ b/src/opencl/cl_program_builder.hpp @@ -76,6 +76,7 @@ namespace spla { }; size_t kernel_group_limit(CLProgramBuilder& builder, CLAccelerator* acc, const char* kernel_name); + uint capped_group_size(CLProgramBuilder& builder, CLAccelerator* acc, const char* kernel_name); /** * @} diff --git a/src/opencl/cl_reduce_by_key.hpp b/src/opencl/cl_reduce_by_key.hpp index 2f60a9f85..d999ced9d 100644 --- a/src/opencl/cl_reduce_by_key.hpp +++ b/src/opencl/cl_reduce_by_key.hpp @@ -59,8 +59,7 @@ namespace spla { const uint block_size = cl_acc->get_default_wgs(); const uint sequential_switch = 32; - const uint kernel_limit = kernel_group_limit(builder, cl_acc, "reduce_by_key_small"); - const uint small_switch = kernel_limit / cl_acc->get_wave_size() * cl_acc->get_wave_size(); + const uint small_switch = capped_group_size(builder, cl_acc, "reduce_by_key_small"); if (size == 0) { reduced_size = 0; diff --git a/src/opencl/cl_sort_by_key.hpp b/src/opencl/cl_sort_by_key.hpp index ff888bb6a..b0fdfe3f1 100644 --- a/src/opencl/cl_sort_by_key.hpp +++ b/src/opencl/cl_sort_by_key.hpp @@ -62,6 +62,12 @@ namespace spla { .set_source(source_sort_bitonic) .acquire(); + const uint local_limit = capped_group_size(builder, acc, "bitonic_sort_local"); + const uint global_limit = capped_group_size(builder, acc, "bitonic_sort_global"); + if (local_limit == 0 || global_limit == 0) { + return false; + } + auto kernel_local = builder.make_kernel("bitonic_sort_local"); kernel_local.setArg(0, keys); kernel_local.setArg(1, values); @@ -69,24 +75,19 @@ namespace spla { if (size <= local_size) { const uint wave_size = acc->get_wave_size(); - const uint n_threads = align(std::min(size, max_treads_per_block), wave_size); + const uint n_threads = std::min(align(std::min(size, max_treads_per_block), wave_size), local_limit); - if (n_threads > kernel_group_limit(builder, acc, "bitonic_sort_local")) { - return false; - } cl::NDRange global(n_threads); cl::NDRange local(n_threads); queue.enqueueNDRangeKernel(kernel_local, cl::NDRange(), global, local); return true; } - const uint n_groups = div_up(size, local_size); + const uint n_groups = div_up(size, local_size); + const uint block_threads = std::min(max_treads_per_block, local_limit); - if (max_treads_per_block > kernel_group_limit(builder, acc, "bitonic_sort_local")) { - return false; - } - cl::NDRange step_pre_sort_global(max_treads_per_block * n_groups); - cl::NDRange step_pre_sort_local(max_treads_per_block); + cl::NDRange step_pre_sort_global(block_threads * n_groups); + cl::NDRange step_pre_sort_local(block_threads); queue.enqueueNDRangeKernel(kernel_local, cl::NDRange(), step_pre_sort_global, step_pre_sort_local); auto kernel_global = builder.make_kernel("bitonic_sort_global"); @@ -95,11 +96,8 @@ namespace spla { kernel_global.setArg(2, size); kernel_global.setArg(3, uint(local_size * 2)); - if (acc->get_max_wgs() > kernel_group_limit(builder, acc, "bitonic_sort_global")) { - return false; - } - cl::NDRange step_final_global(acc->get_max_wgs()); - cl::NDRange step_final_local(acc->get_max_wgs()); + cl::NDRange step_final_global(global_limit); + cl::NDRange step_final_local(global_limit); queue.enqueueNDRangeKernel(kernel_global, cl::NDRange(), step_final_global, step_final_local); return true; }